2 * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 #include "evolution-config.h"
21 #include <webkitdom/webkitdom.h>
23 #include "web-extensions/e-dom-utils.h"
25 #include "e-editor-page.h"
26 #include "e-editor-undo-redo-manager.h"
28 #include "e-editor-dom-functions.h"
30 #define HTML_KEY_CODE_BACKSPACE 8
31 #define HTML_KEY_CODE_RETURN 13
32 #define HTML_KEY_CODE_CONTROL 17
33 #define HTML_KEY_CODE_SPACE 32
34 #define HTML_KEY_CODE_DELETE 46
35 #define HTML_KEY_CODE_TABULATOR 9
37 /* ******************** Tests ******************** */
40 workaround_spaces (const gchar
*text
)
45 tmp
= e_str_replace_string (text
, " ", " ");
47 str
= g_string_free (tmp
, FALSE
);
51 tmp
= e_str_replace_string (text
, " ", " ");
54 str
= g_string_free (tmp
, FALSE
);
56 str
= g_strdup (text
);
63 e_editor_dom_test_html_equal (WebKitDOMDocument
*document
,
67 WebKitDOMElement
*elem1
, *elem2
;
72 g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (document
), FALSE
);
73 g_return_val_if_fail (html1
!= NULL
, FALSE
);
74 g_return_val_if_fail (html2
!= NULL
, FALSE
);
76 elem1
= webkit_dom_document_create_element (document
, "TestHtmlEqual", &error
);
77 if (error
|| !elem1
) {
78 g_warning ("%s: Failed to create elem1: %s", G_STRFUNC
, error
? error
->message
: "Unknown error");
79 g_clear_error (&error
);
83 elem2
= webkit_dom_document_create_element (document
, "TestHtmlEqual", &error
);
84 if (error
|| !elem2
) {
85 g_warning ("%s: Failed to create elem2: %s", G_STRFUNC
, error
? error
->message
: "Unknown error");
86 g_clear_error (&error
);
90 /* FIXME WK2: Workaround when is used instead of regular spaces. (Placed by WebKit?) */
91 str1
= workaround_spaces (html1
);
92 str2
= workaround_spaces (html2
);
94 webkit_dom_element_set_inner_html (elem1
, str1
, &error
);
96 webkit_dom_element_set_inner_html (elem2
, str2
, &error
);
99 webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem1
));
100 webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem2
));
102 res
= webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (elem1
), WEBKIT_DOM_NODE (elem2
));
104 g_warning ("%s: Failed to set inner html2: %s", G_STRFUNC
, error
->message
);
107 g_warning ("%s: Failed to set inner html1: %s", G_STRFUNC
, error
->message
);
110 if (res
&& (g_strcmp0 (html1
, str1
) != 0 || g_strcmp0 (html2
, str2
) != 0))
111 g_warning ("%s: Applied the ' ' workaround", G_STRFUNC
);
113 g_clear_error (&error
);
120 /* ******************** Actions ******************** */
122 static WebKitDOMElement
*
123 get_table_cell_element (EEditorPage
*editor_page
)
125 WebKitDOMDocument
*document
;
126 WebKitDOMElement
*cell
;
127 WebKitDOMNode
*node_under_mouse_click
;
129 document
= e_editor_page_get_document (editor_page
);
130 cell
= webkit_dom_document_get_element_by_id (document
, "-x-evo-current-cell");
135 node_under_mouse_click
= e_editor_page_get_node_under_mouse_click (editor_page
);
137 if (node_under_mouse_click
&& WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node_under_mouse_click
)) {
138 cell
= WEBKIT_DOM_ELEMENT (node_under_mouse_click
);
140 WebKitDOMElement
*selection_start
;
142 e_editor_dom_selection_save (editor_page
);
144 selection_start
= webkit_dom_document_get_element_by_id (
145 document
, "-x-evo-selection-start-marker");
147 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start
), "TD");
149 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start
), "TH");
151 e_editor_dom_selection_restore (editor_page
);
158 prepare_history_for_table (EEditorPage
*editor_page
,
159 WebKitDOMElement
*table
,
160 EEditorHistoryEvent
*ev
)
162 ev
->type
= HISTORY_TABLE_DIALOG
;
164 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
166 ev
->data
.dom
.from
= g_object_ref (webkit_dom_node_clone_node_with_error (
167 WEBKIT_DOM_NODE (table
), TRUE
, NULL
));
172 save_history_for_table (EEditorPage
*editor_page
,
173 WebKitDOMElement
*table
,
174 EEditorHistoryEvent
*ev
)
176 EEditorUndoRedoManager
*manager
;
179 ev
->data
.dom
.to
= g_object_ref (webkit_dom_node_clone_node_with_error (
180 WEBKIT_DOM_NODE (table
), TRUE
, NULL
));
182 ev
->data
.dom
.to
= NULL
;
184 e_editor_dom_selection_get_coordinates (editor_page
,
185 &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
187 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
188 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
192 e_editor_dom_delete_cell_contents (EEditorPage
*editor_page
)
195 WebKitDOMElement
*cell
, *table_cell
, *table
;
196 EEditorHistoryEvent
*ev
= NULL
;
198 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
200 table_cell
= get_table_cell_element (editor_page
);
201 g_return_if_fail (table_cell
!= NULL
);
203 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TD");
205 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TH");
206 g_return_if_fail (cell
!= NULL
);
208 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (cell
), "TABLE");
209 g_return_if_fail (table
!= NULL
);
211 ev
= g_new0 (EEditorHistoryEvent
, 1);
212 prepare_history_for_table (editor_page
, table
, ev
);
214 while ((node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell
))))
217 save_history_for_table (editor_page
, table
, ev
);
221 e_editor_dom_delete_column (EEditorPage
*editor_page
)
223 WebKitDOMElement
*cell
, *table
, *table_cell
;
224 WebKitDOMHTMLCollection
*rows
= NULL
;
225 EEditorHistoryEvent
*ev
= NULL
;
226 gulong index
, length
, ii
;
228 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
230 table_cell
= get_table_cell_element (editor_page
);
231 g_return_if_fail (table_cell
!= NULL
);
233 /* Find TD in which the selection starts */
234 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TD");
236 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TH");
237 g_return_if_fail (cell
!= NULL
);
239 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (cell
), "TABLE");
240 g_return_if_fail (table
!= NULL
);
242 ev
= g_new0 (EEditorHistoryEvent
, 1);
243 prepare_history_for_table (editor_page
, table
, ev
);
245 rows
= webkit_dom_html_table_element_get_rows (
246 WEBKIT_DOM_HTML_TABLE_ELEMENT (table
));
247 length
= webkit_dom_html_collection_get_length (rows
);
249 index
= webkit_dom_html_table_cell_element_get_cell_index (
250 WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell
));
252 for (ii
= 0; ii
< length
; ii
++) {
255 row
= webkit_dom_html_collection_item (rows
, ii
);
257 webkit_dom_html_table_row_element_delete_cell (
258 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
), index
, NULL
);
261 g_clear_object (&rows
);
263 save_history_for_table (editor_page
, table
, ev
);
267 e_editor_dom_delete_row (EEditorPage
*editor_page
)
269 WebKitDOMElement
*row
, *table
, *table_cell
;
270 EEditorHistoryEvent
*ev
= NULL
;
272 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
274 table_cell
= get_table_cell_element (editor_page
);
275 g_return_if_fail (table_cell
!= NULL
);
277 row
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TR");
278 g_return_if_fail (row
!= NULL
);
280 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TABLE");
281 g_return_if_fail (table
!= NULL
);
283 ev
= g_new0 (EEditorHistoryEvent
, 1);
284 prepare_history_for_table (editor_page
, table
, ev
);
286 remove_node (WEBKIT_DOM_NODE (row
));
288 save_history_for_table (editor_page
, table
, ev
);
292 e_editor_dom_delete_table (EEditorPage
*editor_page
)
294 WebKitDOMElement
*table
, *table_cell
;
295 EEditorHistoryEvent
*ev
= NULL
;
297 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
299 table_cell
= get_table_cell_element (editor_page
);
300 g_return_if_fail (table_cell
!= NULL
);
302 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TABLE");
303 g_return_if_fail (table
!= NULL
);
305 ev
= g_new0 (EEditorHistoryEvent
, 1);
306 prepare_history_for_table (editor_page
, table
, ev
);
308 remove_node (WEBKIT_DOM_NODE (table
));
310 save_history_for_table (editor_page
, NULL
, ev
);
314 e_editor_dom_insert_column_after (EEditorPage
*editor_page
)
316 WebKitDOMElement
*cell
, *row
, *table_cell
, *table
;
317 EEditorHistoryEvent
*ev
= NULL
;
320 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
322 table_cell
= get_table_cell_element (editor_page
);
323 g_return_if_fail (table_cell
!= NULL
);
325 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TD");
327 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TH");
328 g_return_if_fail (cell
!= NULL
);
330 row
= dom_node_find_parent_element (WEBKIT_DOM_NODE (cell
), "TR");
331 g_return_if_fail (row
!= NULL
);
333 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TABLE");
334 g_return_if_fail (table
!= NULL
);
336 ev
= g_new0 (EEditorHistoryEvent
, 1);
337 prepare_history_for_table (editor_page
, table
, ev
);
339 /* Get the first row in the table */
340 row
= WEBKIT_DOM_ELEMENT (
341 webkit_dom_node_get_first_child (
342 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row
))));
344 index
= webkit_dom_html_table_cell_element_get_cell_index (
345 WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell
));
348 webkit_dom_html_table_row_element_insert_cell (
349 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
), index
+ 1, NULL
);
351 row
= WEBKIT_DOM_ELEMENT (
352 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row
)));
355 save_history_for_table (editor_page
, table
, ev
);
359 e_editor_dom_insert_column_before (EEditorPage
*editor_page
)
361 WebKitDOMElement
*cell
, *row
, *table_cell
, *table
;
362 EEditorHistoryEvent
*ev
= NULL
;
365 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
367 table_cell
= get_table_cell_element (editor_page
);
368 g_return_if_fail (table_cell
!= NULL
);
370 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TD");
372 cell
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TH");
374 g_return_if_fail (cell
!= NULL
);
376 row
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TR");
377 g_return_if_fail (row
!= NULL
);
379 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TABLE");
380 g_return_if_fail (table
!= NULL
);
382 ev
= g_new0 (EEditorHistoryEvent
, 1);
383 prepare_history_for_table (editor_page
, table
, ev
);
385 /* Get the first row in the table */
386 row
= WEBKIT_DOM_ELEMENT (
387 webkit_dom_node_get_first_child (
388 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row
))));
390 index
= webkit_dom_html_table_cell_element_get_cell_index (
391 WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell
));
394 webkit_dom_html_table_row_element_insert_cell (
395 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
), index
, NULL
);
397 row
= WEBKIT_DOM_ELEMENT (
398 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row
)));
401 save_history_for_table (editor_page
, table
, ev
);
405 e_editor_dom_insert_row_above (EEditorPage
*editor_page
)
407 WebKitDOMElement
*row
, *table
, *table_cell
;
408 WebKitDOMHTMLCollection
*cells
= NULL
;
409 WebKitDOMHTMLElement
*new_row
;
410 EEditorHistoryEvent
*ev
= NULL
;
411 gulong index
, cell_count
, ii
;
413 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
415 table_cell
= get_table_cell_element (editor_page
);
416 g_return_if_fail (table_cell
!= NULL
);
418 row
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TR");
419 g_return_if_fail (row
!= NULL
);
421 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (row
), "TABLE");
422 g_return_if_fail (table
!= NULL
);
424 ev
= g_new0 (EEditorHistoryEvent
, 1);
425 prepare_history_for_table (editor_page
, table
, ev
);
427 index
= webkit_dom_html_table_row_element_get_row_index (
428 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
));
430 new_row
= webkit_dom_html_table_element_insert_row (
431 WEBKIT_DOM_HTML_TABLE_ELEMENT (table
), index
, NULL
);
433 cells
= webkit_dom_html_table_row_element_get_cells (
434 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
));
435 cell_count
= webkit_dom_html_collection_get_length (cells
);
436 for (ii
= 0; ii
< cell_count
; ii
++) {
437 webkit_dom_html_table_row_element_insert_cell (
438 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row
), -1, NULL
);
441 g_clear_object (&cells
);
443 save_history_for_table (editor_page
, table
, ev
);
447 e_editor_dom_insert_row_below (EEditorPage
*editor_page
)
449 WebKitDOMElement
*row
, *table
, *table_cell
;
450 WebKitDOMHTMLCollection
*cells
= NULL
;
451 WebKitDOMHTMLElement
*new_row
;
452 EEditorHistoryEvent
*ev
= NULL
;
453 gulong index
, cell_count
, ii
;
455 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
457 table_cell
= get_table_cell_element (editor_page
);
458 g_return_if_fail (table_cell
!= NULL
);
460 row
= dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell
), "TR");
461 g_return_if_fail (row
!= NULL
);
463 table
= dom_node_find_parent_element (WEBKIT_DOM_NODE (row
), "TABLE");
464 g_return_if_fail (table
!= NULL
);
466 ev
= g_new0 (EEditorHistoryEvent
, 1);
467 prepare_history_for_table (editor_page
, table
, ev
);
469 index
= webkit_dom_html_table_row_element_get_row_index (
470 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
));
472 new_row
= webkit_dom_html_table_element_insert_row (
473 WEBKIT_DOM_HTML_TABLE_ELEMENT (table
), index
+ 1, NULL
);
475 cells
= webkit_dom_html_table_row_element_get_cells (
476 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row
));
477 cell_count
= webkit_dom_html_collection_get_length (cells
);
478 for (ii
= 0; ii
< cell_count
; ii
++) {
479 webkit_dom_html_table_row_element_insert_cell (
480 WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row
), -1, NULL
);
483 g_clear_object (&cells
);
485 save_history_for_table (editor_page
, table
, ev
);
489 e_editor_dom_save_history_for_cut (EEditorPage
*editor_page
)
491 WebKitDOMDocument
*document
;
492 WebKitDOMDocumentFragment
*fragment
;
493 WebKitDOMDOMWindow
*dom_window
= NULL
;
494 WebKitDOMDOMSelection
*dom_selection
= NULL
;
495 WebKitDOMRange
*range
= NULL
;
496 EEditorHistoryEvent
*ev
;
497 EEditorUndoRedoManager
*manager
;
499 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
501 document
= e_editor_page_get_document (editor_page
);
502 dom_window
= webkit_dom_document_get_default_view (document
);
503 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
504 g_clear_object (&dom_window
);
506 if (!webkit_dom_dom_selection_get_range_count (dom_selection
) ||
507 webkit_dom_dom_selection_get_is_collapsed (dom_selection
)) {
508 g_clear_object (&dom_selection
);
512 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
514 ev
= g_new0 (EEditorHistoryEvent
, 1);
515 ev
->type
= HISTORY_DELETE
;
517 e_editor_dom_selection_get_coordinates (editor_page
,
523 ev
->after
.start
.x
= ev
->before
.start
.x
;
524 ev
->after
.start
.y
= ev
->before
.start
.y
;
525 ev
->after
.end
.x
= ev
->before
.start
.x
;
526 ev
->after
.end
.y
= ev
->before
.start
.y
;
528 /* Save the fragment. */
529 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
530 g_clear_object (&dom_selection
);
531 g_clear_object (&range
);
532 ev
->data
.fragment
= g_object_ref (fragment
);
534 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
535 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
536 e_editor_page_set_dont_save_history_in_body_input (editor_page
, TRUE
);
539 /* ******************** View ******************** */
542 * e_editor_dom_exec_command:
543 * @document: a #WebKitDOMDocument
544 * @command: an #EContentEditorCommand to execute
545 * @value: value of the command (or @NULL if the command does not require value)
547 * The function will fail when @value is @NULL or empty but the current @command
548 * requires a value to be passed. The @value is ignored when the @command does
549 * not expect any value.
551 * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise.
554 e_editor_dom_exec_command (EEditorPage
*editor_page
,
555 EContentEditorCommand command
,
558 const gchar
*cmd_str
= 0;
559 gboolean has_value
= FALSE
;
561 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
563 #define CHECK_COMMAND(cmd,str,val) case cmd:\
565 g_return_val_if_fail (value && *value, FALSE);\
572 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR
, "BackColor", TRUE
)
573 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BOLD
, "Bold", FALSE
)
574 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_COPY
, "Copy", FALSE
)
575 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CREATE_LINK
, "CreateLink", TRUE
)
576 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CUT
, "Cut", FALSE
)
577 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR
, "DefaultParagraphSeparator", FALSE
)
578 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DELETE
, "Delete", FALSE
)
579 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FIND_STRING
, "FindString", TRUE
)
580 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_NAME
, "FontName", TRUE
)
581 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE
, "FontSize", TRUE
)
582 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA
, "FontSizeDelta", TRUE
)
583 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORE_COLOR
, "ForeColor", TRUE
)
584 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK
, "FormatBlock", TRUE
)
585 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE
, "ForwardDelete", FALSE
)
586 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_HILITE_COLOR
, "HiliteColor", TRUE
)
587 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INDENT
, "Indent", FALSE
)
588 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE
, "InsertHorizontalRule", FALSE
)
589 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HTML
, "InsertHTML", TRUE
)
590 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE
, "InsertImage", TRUE
)
591 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK
, "InsertLineBreak", FALSE
)
592 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT
, "InsertNewlineInQuotedContent", FALSE
)
593 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST
, "InsertOrderedList", FALSE
)
594 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH
, "InsertParagraph", FALSE
)
595 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_TEXT
, "InsertText", TRUE
)
596 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST
, "InsertUnorderedList", FALSE
)
597 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_ITALIC
, "Italic", FALSE
)
598 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER
, "JustifyCenter", FALSE
)
599 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL
, "JustifyFull", FALSE
)
600 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_LEFT
, "JustifyLeft", FALSE
)
601 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE
, "JustifyNone", FALSE
)
602 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT
, "JustifyRight", FALSE
)
603 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_OUTDENT
, "Outdent", FALSE
)
604 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE
, "Paste", FALSE
)
605 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE
, "PasteAndMatchStyle", FALSE
)
606 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT
, "PasteAsPlainText", FALSE
)
607 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PRINT
, "Print", FALSE
)
608 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REDO
, "Redo", FALSE
)
609 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT
, "RemoveFormat", FALSE
)
610 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SELECT_ALL
, "SelectAll", FALSE
)
611 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
, "Strikethrough", FALSE
)
612 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS
, "StyleWithCSS", TRUE
)
613 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUBSCRIPT
, "Subscript", FALSE
)
614 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT
, "Superscript", FALSE
)
615 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_TRANSPOSE
, "Transpose", FALSE
)
616 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDERLINE
, "Underline", FALSE
)
617 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDO
, "Undo", FALSE
)
618 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNLINK
, "Unlink", FALSE
)
619 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNSELECT
, "Unselect", FALSE
)
620 CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_USE_CSS
, "UseCSS", TRUE
)
623 e_editor_page_set_dont_save_history_in_body_input (editor_page
, TRUE
);
625 return webkit_dom_document_exec_command (
626 e_editor_page_get_document (editor_page
), cmd_str
, FALSE
, has_value
? value
: "" );
630 perform_spell_check (WebKitDOMDOMSelection
*dom_selection
,
631 WebKitDOMRange
*start_range
,
632 WebKitDOMRange
*end_range
)
634 WebKitDOMRange
*actual
= start_range
;
636 /* FIXME WK2: this doesn't work, the cursor is moved, but the spellcheck is not updated */
637 /* Go through all words to spellcheck them. To avoid this we have to wait for
638 * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
639 /* We are moving forward word by word until we hit the text on the end. */
640 while (actual
&& webkit_dom_range_compare_boundary_points (actual
, WEBKIT_DOM_RANGE_START_TO_START
, end_range
, NULL
) < 0) {
641 if (actual
!= start_range
)
642 g_object_unref (actual
);
643 webkit_dom_dom_selection_modify (
644 dom_selection
, "move", "forward", "word");
645 actual
= webkit_dom_dom_selection_get_range_at (
646 dom_selection
, 0, NULL
);
648 g_clear_object (&actual
);
652 e_editor_dom_force_spell_check_for_current_paragraph (EEditorPage
*editor_page
)
654 WebKitDOMDocument
*document
;
655 WebKitDOMDOMSelection
*dom_selection
= NULL
;
656 WebKitDOMDOMWindow
*dom_window
= NULL
;
657 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
658 WebKitDOMElement
*parent
;
659 WebKitDOMHTMLElement
*body
;
660 WebKitDOMRange
*end_range
= NULL
, *actual
= NULL
;
663 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
665 document
= e_editor_page_get_document (editor_page
);
667 if (!e_editor_page_get_inline_spelling_enabled (editor_page
))
670 document
= e_editor_page_get_document (editor_page
);
671 body
= webkit_dom_document_get_body (document
);
676 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)))
679 e_editor_dom_selection_save (editor_page
);
681 selection_start_marker
= webkit_dom_document_get_element_by_id (
682 document
, "-x-evo-selection-start-marker");
683 selection_end_marker
= webkit_dom_document_get_element_by_id (
684 document
, "-x-evo-selection-end-marker");
686 if (!selection_start_marker
|| !selection_end_marker
)
689 /* Block callbacks of selection-changed signal as we don't want to
690 * recount all the block format things in EEditorSelection and here as well
691 * when we are moving with caret */
692 e_editor_page_block_selection_changed (editor_page
);
694 parent
= get_parent_block_element (WEBKIT_DOM_NODE (selection_end_marker
));
696 parent
= WEBKIT_DOM_ELEMENT (body
);
698 /* Append some text on the end of the element */
699 text
= webkit_dom_document_create_text_node (document
, "-x-evo-end");
700 webkit_dom_node_append_child (
701 WEBKIT_DOM_NODE (parent
),
702 WEBKIT_DOM_NODE (text
),
705 parent
= get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker
));
707 parent
= WEBKIT_DOM_ELEMENT (body
);
709 /* Create range that's pointing on the end of this text */
710 end_range
= webkit_dom_document_create_range (document
);
711 webkit_dom_range_select_node_contents (
712 end_range
, WEBKIT_DOM_NODE (text
), NULL
);
713 webkit_dom_range_collapse (end_range
, FALSE
, NULL
);
715 /* Move on the beginning of the paragraph */
716 dom_window
= webkit_dom_document_get_default_view (document
);
717 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
719 actual
= webkit_dom_document_create_range (document
);
720 webkit_dom_range_select_node_contents (
721 actual
, WEBKIT_DOM_NODE (parent
), NULL
);
722 webkit_dom_range_collapse (actual
, TRUE
, NULL
);
723 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
724 webkit_dom_dom_selection_add_range (dom_selection
, actual
);
725 g_clear_object (&actual
);
727 actual
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
728 perform_spell_check (dom_selection
, actual
, end_range
);
730 g_clear_object (&dom_selection
);
731 g_clear_object (&dom_window
);
732 g_clear_object (&end_range
);
733 g_clear_object (&actual
);
735 /* Remove the text that we inserted on the end of the paragraph */
736 remove_node (WEBKIT_DOM_NODE (text
));
738 e_editor_dom_selection_restore (editor_page
);
739 /* Unblock the callbacks */
740 e_editor_page_unblock_selection_changed (editor_page
);
744 refresh_spell_check (EEditorPage
*editor_page
,
745 gboolean enable_spell_check
)
747 WebKitDOMDocument
*document
;
748 WebKitDOMDOMSelection
*dom_selection
= NULL
;
749 WebKitDOMDOMWindow
*dom_window
= NULL
;
750 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
751 WebKitDOMHTMLElement
*body
;
752 WebKitDOMRange
*end_range
= NULL
, *actual
= NULL
;
755 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
757 document
= e_editor_page_get_document (editor_page
);
758 body
= webkit_dom_document_get_body (document
);
760 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)))
763 /* Enable/Disable spellcheck in composer */
764 webkit_dom_element_set_attribute (
765 WEBKIT_DOM_ELEMENT (body
),
767 enable_spell_check
? "true" : "false",
770 e_editor_dom_selection_save (editor_page
);
772 selection_start_marker
= webkit_dom_document_get_element_by_id (
773 document
, "-x-evo-selection-start-marker");
774 selection_end_marker
= webkit_dom_document_get_element_by_id (
775 document
, "-x-evo-selection-end-marker");
777 /* Sometimes the web view is not focused, so we have to save the selection
778 * manually into the body */
779 if (!selection_start_marker
|| !selection_end_marker
) {
780 WebKitDOMNode
*child
;
782 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
783 if (!WEBKIT_DOM_IS_ELEMENT (child
))
786 dom_add_selection_markers_into_element_start (
788 WEBKIT_DOM_ELEMENT (child
),
789 &selection_start_marker
,
790 &selection_end_marker
);
793 /* Block callbacks of selection-changed signal as we don't want to
794 * recount all the block format things in EEditorSelection and here as well
795 * when we are moving with caret */
796 e_editor_page_block_selection_changed (editor_page
);
798 /* Append some text on the end of the body */
799 text
= webkit_dom_document_create_text_node (document
, "-x-evo-end");
800 webkit_dom_node_append_child (
801 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (text
), NULL
);
803 /* Create range that's pointing on the end of this text */
804 end_range
= webkit_dom_document_create_range (document
);
805 webkit_dom_range_select_node_contents (
806 end_range
, WEBKIT_DOM_NODE (text
), NULL
);
807 webkit_dom_range_collapse (end_range
, FALSE
, NULL
);
809 dom_window
= webkit_dom_document_get_default_view (document
);
810 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
812 /* Move on the beginning of the document */
813 webkit_dom_dom_selection_modify (
814 dom_selection
, "move", "backward", "documentboundary");
816 actual
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
817 perform_spell_check (dom_selection
, actual
, end_range
);
819 g_clear_object (&dom_selection
);
820 g_clear_object (&dom_window
);
821 g_clear_object (&end_range
);
822 g_clear_object (&actual
);
824 /* Remove the text that we inserted on the end of the body */
825 remove_node (WEBKIT_DOM_NODE (text
));
827 e_editor_dom_selection_restore (editor_page
);
828 /* Unblock the callbacks */
829 e_editor_page_unblock_selection_changed (editor_page
);
833 e_editor_dom_turn_spell_check_off (EEditorPage
*editor_page
)
835 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
837 refresh_spell_check (editor_page
, FALSE
);
841 e_editor_dom_force_spell_check_in_viewport (EEditorPage
*editor_page
)
843 WebKitDOMDocument
*document
;
844 WebKitDOMDOMSelection
*dom_selection
= NULL
;
845 WebKitDOMDOMWindow
*dom_window
= NULL
;
846 WebKitDOMElement
*last_element
;
847 WebKitDOMHTMLElement
*body
;
848 WebKitDOMRange
*end_range
= NULL
, *actual
= NULL
;
850 glong viewport_height
;
852 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
854 if (!e_editor_page_get_inline_spelling_enabled (editor_page
))
857 document
= e_editor_page_get_document (editor_page
);
858 body
= webkit_dom_document_get_body (document
);
860 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)))
863 e_editor_dom_selection_save (editor_page
);
865 /* Block callbacks of selection-changed signal as we don't want to
866 * recount all the block format things in EEditorSelection and here as well
867 * when we are moving with caret */
868 e_editor_page_block_selection_changed (editor_page
);
870 /* We have to add 10 px offset as otherwise just the HTML element will be returned */
871 actual
= webkit_dom_document_caret_range_from_point (document
, 10, 10);
875 /* Append some text on the end of the body */
876 text
= webkit_dom_document_create_text_node (document
, "-x-evo-end");
878 dom_window
= webkit_dom_document_get_default_view (document
);
879 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
881 /* We have to add 10 px offset as otherwise just the HTML element will be returned */
882 viewport_height
= webkit_dom_dom_window_get_inner_height (dom_window
);
883 last_element
= webkit_dom_document_element_from_point (document
, 10, viewport_height
- 10);
884 if (last_element
&& !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (last_element
) &&
885 !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (last_element
)) {
886 WebKitDOMElement
*parent
;
888 parent
= get_parent_block_element (WEBKIT_DOM_NODE (last_element
));
889 webkit_dom_node_append_child (
890 WEBKIT_DOM_NODE (parent
? parent
: last_element
), WEBKIT_DOM_NODE (text
), NULL
);
892 webkit_dom_node_append_child (
893 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (text
), NULL
);
895 /* Create range that's pointing on the end of viewport */
896 end_range
= webkit_dom_document_create_range (document
);
897 webkit_dom_range_select_node_contents (
898 end_range
, WEBKIT_DOM_NODE (text
), NULL
);
899 webkit_dom_range_collapse (end_range
, FALSE
, NULL
);
901 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
902 webkit_dom_dom_selection_add_range (dom_selection
, actual
);
903 perform_spell_check (dom_selection
, actual
, end_range
);
905 g_clear_object (&dom_selection
);
906 g_clear_object (&dom_window
);
907 g_clear_object (&end_range
);
908 g_clear_object (&actual
);
910 /* Remove the text that we inserted on the end of the body */
911 remove_node (WEBKIT_DOM_NODE (text
));
914 e_editor_dom_selection_restore (editor_page
);
915 /* Unblock the callbacks */
916 e_editor_page_unblock_selection_changed (editor_page
);
920 e_editor_dom_force_spell_check (EEditorPage
*editor_page
)
922 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
924 if (e_editor_page_get_inline_spelling_enabled (editor_page
))
925 refresh_spell_check (editor_page
, TRUE
);
929 e_editor_dom_node_is_citation_node (WebKitDOMNode
*node
)
931 gboolean ret_val
= FALSE
;
934 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
))
937 /* citation == <blockquote type='cite'> */
938 if ((value
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "type")))
939 ret_val
= g_strcmp0 (value
, "cite") == 0;
947 e_editor_dom_get_citation_level (WebKitDOMNode
*node
)
949 WebKitDOMNode
*parent
= node
;
952 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
953 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent
) &&
954 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "type"))
957 parent
= webkit_dom_node_get_parent_node (parent
);
964 get_quotation_for_level (gint quote_level
)
966 const gchar
*quote_element
= "<span class=\"-x-evo-quote-character\">" QUOTE_SYMBOL
" </span>";
968 GString
*output
= g_string_new ("");
970 for (ii
= 0; ii
< quote_level
; ii
++)
971 g_string_append (output
, quote_element
);
973 return g_string_free (output
, FALSE
);
977 e_editor_dom_quote_plain_text_element_after_wrapping (EEditorPage
*editor_page
,
978 WebKitDOMElement
*element
,
981 WebKitDOMDocument
*document
;
982 WebKitDOMNodeList
*list
= NULL
;
983 WebKitDOMNode
*quoted_node
;
987 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
988 g_return_if_fail (element
!= NULL
);
990 document
= e_editor_page_get_document (editor_page
);
992 quoted_node
= WEBKIT_DOM_NODE (
993 webkit_dom_document_create_element (document
, "SPAN", NULL
));
994 webkit_dom_element_set_class_name (
995 WEBKIT_DOM_ELEMENT (quoted_node
), "-x-evo-quoted");
996 quotation
= get_quotation_for_level (quote_level
);
997 webkit_dom_element_set_inner_html (
998 WEBKIT_DOM_ELEMENT (quoted_node
), quotation
, NULL
);
1000 list
= webkit_dom_element_query_selector_all (
1001 element
, "br.-x-evo-wrap-br, pre > br", NULL
);
1002 webkit_dom_node_insert_before (
1003 WEBKIT_DOM_NODE (element
),
1005 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
)),
1008 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
1009 WebKitDOMNode
*br
= webkit_dom_node_list_item (list
, ii
);
1010 WebKitDOMNode
*prev_sibling
= webkit_dom_node_get_previous_sibling (br
);
1012 if ((!WEBKIT_DOM_IS_ELEMENT (prev_sibling
) ||
1013 !element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-quoted")) &&
1014 webkit_dom_node_get_next_sibling (br
)) {
1016 webkit_dom_node_insert_before (
1017 webkit_dom_node_get_parent_node (br
),
1018 webkit_dom_node_clone_node_with_error (quoted_node
, TRUE
, NULL
),
1019 webkit_dom_node_get_next_sibling (br
),
1024 g_clear_object (&list
);
1029 return_pressed_in_empty_line (EEditorPage
*editor_page
)
1031 WebKitDOMNode
*node
;
1032 WebKitDOMRange
*range
= NULL
;
1034 range
= e_editor_dom_get_current_range (editor_page
);
1038 node
= webkit_dom_range_get_start_container (range
, NULL
);
1039 if (!WEBKIT_DOM_IS_TEXT (node
)) {
1040 WebKitDOMNode
*first_child
;
1042 first_child
= webkit_dom_node_get_first_child (node
);
1043 if (first_child
&& WEBKIT_DOM_IS_ELEMENT (first_child
) &&
1044 element_has_class (WEBKIT_DOM_ELEMENT (first_child
), "-x-evo-quoted")) {
1045 WebKitDOMNode
*prev_sibling
;
1047 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
1048 if (!prev_sibling
) {
1051 collapsed
= webkit_dom_range_get_collapsed (range
, NULL
);
1052 g_clear_object (&range
);
1058 g_clear_object (&range
);
1064 e_editor_dom_get_parent_block_node_from_child (WebKitDOMNode
*node
)
1066 WebKitDOMNode
*parent
= node
;
1068 if (!WEBKIT_DOM_IS_ELEMENT (parent
) ||
1069 e_editor_dom_is_selection_position_node (parent
))
1070 parent
= webkit_dom_node_get_parent_node (parent
);
1072 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-quoted") ||
1073 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-quote-character") ||
1074 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-signature") ||
1075 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-resizable-wrapper") ||
1076 WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
) ||
1077 element_has_tag (WEBKIT_DOM_ELEMENT (parent
), "b") ||
1078 element_has_tag (WEBKIT_DOM_ELEMENT (parent
), "i") ||
1079 element_has_tag (WEBKIT_DOM_ELEMENT (parent
), "u"))
1080 parent
= webkit_dom_node_get_parent_node (parent
);
1082 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-quoted") ||
1083 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "Apple-tab-span") ||
1084 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-resizable-wrapper"))
1085 parent
= webkit_dom_node_get_parent_node (parent
);
1091 e_editor_dom_node_is_paragraph (WebKitDOMNode
*node
)
1093 if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node
))
1096 return webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node
), "data-evo-paragraph");
1100 e_editor_dom_wrap_and_quote_element (EEditorPage
*editor_page
,
1101 WebKitDOMElement
*element
)
1103 gint citation_level
;
1104 WebKitDOMElement
*tmp_element
= element
;
1106 g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (element
), element
);
1108 if (e_editor_page_get_html_mode (editor_page
))
1111 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
1113 e_editor_dom_remove_quoting_from_element (element
);
1114 e_editor_dom_remove_wrapping_from_element (element
);
1116 if (e_editor_dom_node_is_paragraph (WEBKIT_DOM_NODE (element
))) {
1117 gint word_wrap_length
, length
;
1119 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
1120 length
= word_wrap_length
- 2 * citation_level
;
1121 tmp_element
= e_editor_dom_wrap_paragraph_length (
1122 editor_page
, element
, length
);
1125 if (citation_level
> 0) {
1126 webkit_dom_node_normalize (WEBKIT_DOM_NODE (tmp_element
));
1127 e_editor_dom_quote_plain_text_element_after_wrapping (
1128 editor_page
, tmp_element
, citation_level
);
1135 e_editor_dom_insert_new_line_into_citation (EEditorPage
*editor_page
,
1136 const gchar
*html_to_insert
)
1138 WebKitDOMDocument
*document
;
1139 WebKitDOMElement
*element
, *paragraph
= NULL
;
1140 WebKitDOMNode
*last_block
;
1141 gboolean html_mode
= FALSE
, ret_val
, avoid_editor_call
;
1143 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
1145 document
= e_editor_page_get_document (editor_page
);
1146 html_mode
= e_editor_page_get_html_mode (editor_page
);
1148 avoid_editor_call
= return_pressed_in_empty_line (editor_page
);
1150 if (avoid_editor_call
) {
1151 WebKitDOMElement
*selection_start_marker
;
1152 WebKitDOMNode
*current_block
, *parent
, *parent_block
, *block_clone
;
1154 e_editor_dom_selection_save (editor_page
);
1156 selection_start_marker
= webkit_dom_document_get_element_by_id (
1157 document
, "-x-evo-selection-start-marker");
1159 current_block
= e_editor_dom_get_parent_block_node_from_child (
1160 WEBKIT_DOM_NODE (selection_start_marker
));
1162 block_clone
= webkit_dom_node_clone_node_with_error (current_block
, TRUE
, NULL
);
1163 /* Find selection start marker and restore it after the new line
1165 selection_start_marker
= webkit_dom_element_query_selector (
1166 WEBKIT_DOM_ELEMENT (block_clone
), "#-x-evo-selection-start-marker", NULL
);
1168 /* Find parent node that is immediate child of the BODY */
1169 /* Build the same structure of parent nodes of the current block */
1170 parent_block
= current_block
;
1171 parent
= webkit_dom_node_get_parent_node (parent_block
);
1172 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
1173 WebKitDOMNode
*node
;
1175 parent_block
= parent
;
1176 node
= webkit_dom_node_clone_node_with_error (parent_block
, FALSE
, NULL
);
1177 webkit_dom_node_append_child (node
, block_clone
, NULL
);
1179 parent
= webkit_dom_node_get_parent_node (parent_block
);
1182 paragraph
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
1184 webkit_dom_node_append_child (
1185 WEBKIT_DOM_NODE (paragraph
),
1187 webkit_dom_document_create_element (document
, "BR", NULL
)),
1190 /* Insert the selection markers to right place */
1191 webkit_dom_node_insert_before (
1192 WEBKIT_DOM_NODE (paragraph
),
1193 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker
)),
1194 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph
)),
1196 webkit_dom_node_insert_before (
1197 WEBKIT_DOM_NODE (paragraph
),
1198 WEBKIT_DOM_NODE (selection_start_marker
),
1199 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph
)),
1202 /* Insert the cloned nodes before the BODY parent node */
1203 webkit_dom_node_insert_before (
1204 webkit_dom_node_get_parent_node (parent_block
),
1209 /* Insert the new empty paragraph before the BODY parent node */
1210 webkit_dom_node_insert_before (
1211 webkit_dom_node_get_parent_node (parent_block
),
1212 WEBKIT_DOM_NODE (paragraph
),
1216 /* Remove the old block (its copy was moved to the right place) */
1217 remove_node (current_block
);
1219 e_editor_dom_selection_restore (editor_page
);
1223 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
1224 e_editor_page_block_selection_changed (editor_page
);
1226 ret_val
= e_editor_dom_exec_command (
1227 editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT
, NULL
);
1229 e_editor_page_unblock_selection_changed (editor_page
);
1230 e_editor_dom_register_input_event_listener_on_body (editor_page
);
1235 element
= webkit_dom_document_query_selector (
1236 document
, "body>br", NULL
);
1242 last_block
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
1243 while (last_block
&& e_editor_dom_node_is_citation_node (last_block
))
1244 last_block
= webkit_dom_node_get_last_child (last_block
);
1247 WebKitDOMNode
*last_child
;
1249 if ((last_child
= webkit_dom_node_get_last_child (last_block
))) {
1250 if (WEBKIT_DOM_IS_ELEMENT (last_child
) &&
1251 element_has_class (WEBKIT_DOM_ELEMENT (last_child
), "-x-evo-quoted"))
1252 webkit_dom_node_append_child (
1255 webkit_dom_document_create_element (
1256 document
, "br", NULL
)),
1262 WebKitDOMNode
*sibling
;
1264 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
1266 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (sibling
)) {
1267 WebKitDOMNode
*node
;
1269 node
= webkit_dom_node_get_first_child (sibling
);
1270 while (node
&& e_editor_dom_node_is_citation_node (node
))
1271 node
= webkit_dom_node_get_first_child (node
);
1273 /* Rewrap and requote nodes that were created by split. */
1274 if (WEBKIT_DOM_IS_ELEMENT (node
))
1275 e_editor_dom_wrap_and_quote_element (editor_page
, WEBKIT_DOM_ELEMENT (node
));
1277 if (WEBKIT_DOM_IS_ELEMENT (last_block
))
1278 e_editor_dom_wrap_and_quote_element (editor_page
, WEBKIT_DOM_ELEMENT (last_block
));
1280 e_editor_dom_force_spell_check_in_viewport (editor_page
);
1284 if (html_to_insert
&& *html_to_insert
) {
1285 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, FALSE
);
1286 webkit_dom_element_set_inner_html (
1287 paragraph
, html_to_insert
, NULL
);
1288 if (!webkit_dom_element_query_selector (paragraph
, "#-x-evo-selection-start-marker", NULL
))
1289 dom_add_selection_markers_into_element_end (
1290 document
, paragraph
, NULL
, NULL
);
1292 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
1294 webkit_dom_node_insert_before (
1295 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
1296 WEBKIT_DOM_NODE (paragraph
),
1297 WEBKIT_DOM_NODE (element
),
1300 remove_node (WEBKIT_DOM_NODE (element
));
1302 e_editor_dom_selection_restore (editor_page
);
1307 /* For purpose of this function see e-mail-formatter-quote.c */
1309 put_body_in_citation (WebKitDOMDocument
*document
)
1311 WebKitDOMElement
*cite_body
= webkit_dom_document_query_selector (
1312 document
, "span.-x-evo-cite-body", NULL
);
1315 WebKitDOMHTMLElement
*body
= webkit_dom_document_get_body (document
);
1316 WebKitDOMNode
*citation
;
1317 WebKitDOMNode
*sibling
;
1319 citation
= WEBKIT_DOM_NODE (
1320 webkit_dom_document_create_element (document
, "blockquote", NULL
));
1321 webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (citation
), "-x-evo-main-cite");
1322 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (citation
), "type", "cite", NULL
);
1324 webkit_dom_node_insert_before (
1325 WEBKIT_DOM_NODE (body
),
1327 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)),
1330 while ((sibling
= webkit_dom_node_get_next_sibling (citation
)))
1331 webkit_dom_node_append_child (citation
, sibling
, NULL
);
1333 remove_node (WEBKIT_DOM_NODE (cite_body
));
1337 /* For purpose of this function see e-mail-formatter-quote.c */
1339 move_elements_to_body (EEditorPage
*editor_page
)
1341 WebKitDOMDocument
*document
;
1342 WebKitDOMHTMLElement
*body
;
1343 WebKitDOMNodeList
*list
= NULL
;
1346 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1348 document
= e_editor_page_get_document (editor_page
);
1349 body
= webkit_dom_document_get_body (document
);
1350 list
= webkit_dom_document_query_selector_all (
1351 document
, "div[data-headers]", NULL
);
1352 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
1353 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
1355 webkit_dom_element_remove_attribute (
1356 WEBKIT_DOM_ELEMENT (node
), "data-headers");
1357 webkit_dom_node_insert_before (
1358 WEBKIT_DOM_NODE (body
),
1360 webkit_dom_node_get_first_child (
1361 WEBKIT_DOM_NODE (body
)),
1365 g_clear_object (&list
);
1367 list
= webkit_dom_document_query_selector_all (
1368 document
, "span.-x-evo-to-body[data-credits]", NULL
);
1369 e_editor_page_set_allow_top_signature (editor_page
, webkit_dom_node_list_get_length (list
) > 0);
1370 for (jj
= 0, ii
= webkit_dom_node_list_get_length (list
); ii
--; jj
++) {
1372 WebKitDOMElement
*element
;
1373 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, jj
);
1375 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
1376 credits
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "data-credits");
1378 webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element
), credits
, NULL
);
1381 webkit_dom_node_insert_before (
1382 WEBKIT_DOM_NODE (body
),
1383 WEBKIT_DOM_NODE (element
),
1384 webkit_dom_node_get_first_child (
1385 WEBKIT_DOM_NODE (body
)),
1390 g_clear_object (&list
);
1394 repair_blockquotes (WebKitDOMDocument
*document
)
1396 WebKitDOMHTMLCollection
*collection
;
1399 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (
1400 document
, "gmail_quote");
1401 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
1402 WebKitDOMNode
*node
= webkit_dom_html_collection_item (collection
, ii
);
1404 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
))
1407 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "class");
1408 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "style");
1409 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "type", "cite", NULL
);
1411 if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node
)) &&
1412 webkit_dom_node_get_next_sibling (node
))
1413 webkit_dom_node_append_child (
1416 webkit_dom_document_create_element (
1417 document
, "br", NULL
)),
1420 g_clear_object (&collection
);
1422 collection
= webkit_dom_document_get_elements_by_tag_name_as_html_collection (document
, "blockquote");
1423 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
1424 WebKitDOMNode
*node
= webkit_dom_html_collection_item (collection
, ii
);
1426 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
))
1429 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "class");
1430 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "style");
1431 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "type", "cite", NULL
);
1433 g_clear_object (&collection
);
1437 style_blockquotes (WebKitDOMElement
*element
)
1439 WebKitDOMNodeList
*list
;
1442 g_return_if_fail (WEBKIT_DOM_IS_ELEMENT (element
));
1444 list
= webkit_dom_element_query_selector_all (element
, "blockquote", NULL
);
1445 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
1446 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
1448 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
))
1451 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "style", E_EVOLUTION_BLOCKQUOTE_STYLE
, NULL
);
1453 g_clear_object (&list
);
1457 remove_thunderbird_signature (WebKitDOMDocument
*document
)
1459 WebKitDOMElement
*signature
;
1461 signature
= webkit_dom_document_query_selector (
1462 document
, "pre.moz-signature", NULL
);
1464 remove_node (WEBKIT_DOM_NODE (signature
));
1468 e_editor_dom_check_magic_links (EEditorPage
*editor_page
,
1469 gboolean include_space_by_user
)
1471 WebKitDOMDocument
*document
;
1472 WebKitDOMNode
*node
;
1473 WebKitDOMRange
*range
= NULL
;
1476 gboolean include_space
= FALSE
;
1477 gboolean is_email_address
= FALSE
;
1478 gboolean return_key_pressed
;
1479 GRegex
*regex
= NULL
;
1480 GMatchInfo
*match_info
;
1481 gint start_pos_url
, end_pos_url
;
1482 gboolean has_selection
;
1484 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1486 if (!e_editor_page_get_magic_links_enabled (editor_page
))
1489 return_key_pressed
= e_editor_page_get_return_key_pressed (editor_page
);
1490 document
= e_editor_page_get_document (editor_page
);
1492 if (include_space_by_user
)
1493 include_space
= TRUE
;
1495 include_space
= e_editor_page_get_space_key_pressed (editor_page
);
1497 range
= e_editor_dom_get_current_range (editor_page
);
1498 node
= webkit_dom_range_get_end_container (range
, NULL
);
1499 has_selection
= !webkit_dom_range_get_collapsed (range
, NULL
);
1500 g_clear_object (&range
);
1502 if (return_key_pressed
) {
1503 WebKitDOMNode
* block
;
1505 block
= e_editor_dom_get_parent_block_node_from_child (node
);
1506 /* Get previous block */
1507 if (!(block
= webkit_dom_node_get_previous_sibling (block
)))
1510 /* If block is quoted content, get the last block there */
1511 while (block
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (block
))
1512 block
= webkit_dom_node_get_last_child (block
);
1514 /* Get the last non-empty node */
1515 node
= webkit_dom_node_get_last_child (block
);
1516 if (WEBKIT_DOM_IS_CHARACTER_DATA (node
) &&
1517 webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
)) == 0)
1518 node
= webkit_dom_node_get_previous_sibling (node
);
1520 e_editor_dom_selection_save (editor_page
);
1521 if (has_selection
) {
1522 WebKitDOMElement
*selection_end_marker
;
1524 selection_end_marker
= webkit_dom_document_get_element_by_id (
1525 document
, "-x-evo-selection-end-marker");
1527 node
= webkit_dom_node_get_previous_sibling (
1528 WEBKIT_DOM_NODE (selection_end_marker
));
1532 if (!node
|| WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
))
1535 if (!WEBKIT_DOM_IS_TEXT (node
)) {
1536 if (webkit_dom_node_has_child_nodes (node
))
1537 node
= webkit_dom_node_get_first_child (node
);
1538 if (!WEBKIT_DOM_IS_TEXT (node
))
1542 node_text
= webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node
));
1543 if (!(node_text
&& *node_text
) || !g_utf8_validate (node_text
, -1, NULL
)) {
1548 if (strstr (node_text
, "@") && !strstr (node_text
, "://")) {
1549 is_email_address
= TRUE
;
1550 regex
= g_regex_new (include_space
? E_MAIL_PATTERN_SPACE
: E_MAIL_PATTERN
, 0, 0, NULL
);
1552 regex
= g_regex_new (include_space
? URL_PATTERN_SPACE
: URL_PATTERN
, 0, 0, NULL
);
1559 g_regex_match_all (regex
, node_text
, G_REGEX_MATCH_NOTEMPTY
, &match_info
);
1560 urls
= g_match_info_fetch_all (match_info
);
1563 const gchar
*end_of_match
= NULL
;
1564 gchar
*final_url
= NULL
, *url_end_raw
, *url_text
;
1565 glong url_start
, url_end
, url_length
;
1566 WebKitDOMNode
*url_text_node
;
1567 WebKitDOMElement
*anchor
;
1569 g_match_info_fetch_pos (match_info
, 0, &start_pos_url
, &end_pos_url
);
1571 /* Get start and end position of URL in node's text because positions
1572 * that we get from g_match_info_fetch_pos are not UTF-8 aware */
1573 url_end_raw
= g_strndup (node_text
, end_pos_url
);
1574 url_end
= g_utf8_strlen (url_end_raw
, -1);
1575 url_length
= g_utf8_strlen (urls
[0], -1);
1577 end_of_match
= url_end_raw
+ end_pos_url
- (include_space
? 3 : 2);
1578 /* URLs are extremely unlikely to end with any punctuation, so
1579 * strip any trailing punctuation off from link and put it after
1580 * the link. Do the same for any closing double-quotes as well. */
1581 while (end_of_match
&& end_of_match
!= url_end_raw
&& strchr (URL_INVALID_TRAILING_CHARS
, *end_of_match
)) {
1587 url_start
= url_end
- url_length
;
1589 webkit_dom_text_split_text (
1590 WEBKIT_DOM_TEXT (node
),
1591 include_space
? url_end
- 1 : url_end
,
1594 webkit_dom_text_split_text (
1595 WEBKIT_DOM_TEXT (node
), url_start
, NULL
);
1596 url_text_node
= webkit_dom_node_get_next_sibling (node
);
1598 url_text
= webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (url_text_node
));
1603 if (!url_text
|| !*url_text
|| strrchr (url_text
, ' '))
1606 if (g_str_has_prefix (url_text
, "www."))
1607 final_url
= g_strconcat ("http://" , url_text
, NULL
);
1608 else if (is_email_address
)
1609 final_url
= g_strconcat ("mailto:" , url_text
, NULL
);
1611 final_url
= g_strdup (url_text
);
1613 /* Create and prepare new anchor element */
1614 anchor
= webkit_dom_document_create_element (document
, "A", NULL
);
1616 webkit_dom_element_set_inner_html (anchor
, url_text
, NULL
);
1618 webkit_dom_html_anchor_element_set_href (
1619 WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor
),
1622 /* Insert new anchor element into document */
1623 webkit_dom_node_replace_child (
1624 webkit_dom_node_get_parent_node (node
),
1625 WEBKIT_DOM_NODE (anchor
),
1626 WEBKIT_DOM_NODE (url_text_node
),
1630 g_free (url_end_raw
);
1635 gboolean appending_to_link
= FALSE
;
1636 gchar
*href
, *text
, *url
, *text_to_append
= NULL
;
1638 WebKitDOMElement
*parent
;
1639 WebKitDOMNode
*prev_sibling
;
1641 parent
= webkit_dom_node_get_parent_element (node
);
1642 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
1644 /* If previous sibling is ANCHOR and actual text node is not beginning with
1645 * space => we're appending to link */
1646 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling
)) {
1647 text_to_append
= webkit_dom_node_get_text_content (node
);
1648 if (text_to_append
&& *text_to_append
&&
1649 !strstr (text_to_append
, " ") &&
1650 !(strchr (URL_INVALID_TRAILING_CHARS
, *text_to_append
) &&
1651 !(*text_to_append
== '?' && strlen(text_to_append
) > 1)) &&
1652 !g_str_has_prefix (text_to_append
, UNICODE_NBSP
)) {
1654 appending_to_link
= TRUE
;
1655 parent
= WEBKIT_DOM_ELEMENT (prev_sibling
);
1656 /* If the node(text) contains the some of unwanted characters
1657 * split it into two nodes and select the right one. */
1658 if (g_str_has_suffix (text_to_append
, UNICODE_NBSP
) ||
1659 g_str_has_suffix (text_to_append
, UNICODE_ZERO_WIDTH_SPACE
)) {
1660 webkit_dom_text_split_text (
1661 WEBKIT_DOM_TEXT (node
),
1662 g_utf8_strlen (text_to_append
, -1) - 1,
1664 g_free (text_to_append
);
1665 text_to_append
= webkit_dom_node_get_text_content (node
);
1670 /* If parent is ANCHOR => we're editing the link */
1671 if ((!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
) && !appending_to_link
) || !text_to_append
) {
1672 g_match_info_free (match_info
);
1673 g_regex_unref (regex
);
1675 g_free (text_to_append
);
1679 /* edit only if href and description are the same */
1680 href
= webkit_dom_html_anchor_element_get_href (
1681 WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent
));
1683 if (appending_to_link
) {
1687 webkit_dom_html_element_get_inner_text (
1688 WEBKIT_DOM_HTML_ELEMENT (parent
)),
1690 text
= g_strconcat (inner_text
, text_to_append
, NULL
);
1691 g_free (inner_text
);
1693 text
= webkit_dom_html_element_get_inner_text (
1694 WEBKIT_DOM_HTML_ELEMENT (parent
));
1696 element_remove_class (parent
, "-x-evo-visited-link");
1698 if (strstr (href
, "://") && !strstr (text
, "://")) {
1699 url
= strstr (href
, "://") + 3;
1700 diff
= strlen (text
) - strlen (url
);
1702 if (text
[strlen (text
) - 1] != '/')
1705 if ((g_strcmp0 (url
, text
) != 0 && ABS (diff
) == 1) || appending_to_link
) {
1708 new_href
= g_strconcat (href
, appending_to_link
? "" : text_to_append
, NULL
);
1710 webkit_dom_html_anchor_element_set_href (
1711 WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent
),
1714 if (appending_to_link
) {
1715 webkit_dom_element_insert_adjacent_html (
1716 WEBKIT_DOM_ELEMENT (parent
),
1727 diff
= strlen (text
) - strlen (href
);
1728 if (text
[strlen (text
) - 1] != '/')
1731 if ((g_strcmp0 (href
, text
) != 0 && ABS (diff
) == 1) || appending_to_link
) {
1735 inner_html
= webkit_dom_element_get_inner_html (parent
);
1736 new_href
= g_strconcat (
1738 appending_to_link
? text_to_append
: "",
1741 webkit_dom_html_anchor_element_set_href (
1742 WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent
),
1745 if (appending_to_link
) {
1746 webkit_dom_element_insert_adjacent_html (
1747 WEBKIT_DOM_ELEMENT (parent
),
1756 g_free (inner_html
);
1760 g_free (text_to_append
);
1765 g_match_info_free (match_info
);
1766 g_regex_unref (regex
);
1770 if (!return_key_pressed
)
1771 e_editor_dom_selection_restore (editor_page
);
1775 e_editor_dom_embed_style_sheet (EEditorPage
*editor_page
,
1776 const gchar
*style_sheet_content
)
1778 WebKitDOMDocument
*document
;
1779 WebKitDOMElement
*sheet
;
1781 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1783 document
= e_editor_page_get_document (editor_page
);
1785 e_dom_utils_create_and_add_css_style_sheet (document
, "-x-evo-composer-sheet");
1787 sheet
= webkit_dom_document_get_element_by_id (document
, "-x-evo-composer-sheet");
1788 webkit_dom_element_set_attribute (
1794 webkit_dom_element_set_inner_html (sheet
, style_sheet_content
, NULL
);
1798 e_editor_dom_remove_embedded_style_sheet (EEditorPage
*editor_page
)
1800 WebKitDOMDocument
*document
;
1801 WebKitDOMElement
*sheet
;
1803 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1805 document
= e_editor_page_get_document (editor_page
);
1807 sheet
= webkit_dom_document_get_element_by_id (
1808 document
, "-x-evo-composer-sheet");
1811 remove_node (WEBKIT_DOM_NODE (sheet
));
1815 insert_delete_event (EEditorPage
*editor_page
,
1816 WebKitDOMRange
*range
)
1818 EEditorHistoryEvent
*ev
;
1819 WebKitDOMDocumentFragment
*fragment
;
1820 EEditorUndoRedoManager
*manager
;
1822 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1824 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
1826 if (e_editor_undo_redo_manager_is_operation_in_progress (manager
))
1829 ev
= g_new0 (EEditorHistoryEvent
, 1);
1830 ev
->type
= HISTORY_DELETE
;
1832 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
1833 ev
->data
.fragment
= g_object_ref (fragment
);
1835 e_editor_dom_selection_get_coordinates (editor_page
,
1836 &ev
->before
.start
.x
,
1837 &ev
->before
.start
.y
,
1841 ev
->after
.start
.x
= ev
->before
.start
.x
;
1842 ev
->after
.start
.y
= ev
->before
.start
.y
;
1843 ev
->after
.end
.x
= ev
->before
.start
.x
;
1844 ev
->after
.end
.y
= ev
->before
.start
.y
;
1846 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
1848 ev
= g_new0 (EEditorHistoryEvent
, 1);
1849 ev
->type
= HISTORY_AND
;
1851 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
1854 /* Based on original use_pictograms() from GtkHTML */
1855 static const gchar
*emoticons_chars
=
1856 /* 0 */ "DO)(|/PQ*!"
1857 /* 10 */ "S\0:-\0:\0:-\0"
1858 /* 20 */ ":\0:;=-\"\0:;"
1859 /* 30 */ "B\"|\0:-'\0:X"
1860 /* 40 */ "\0:\0:-\0:\0:-"
1861 /* 50 */ "\0:\0:-\0:\0:-"
1862 /* 60 */ "\0:\0:\0:-\0:\0"
1863 /* 70 */ ":-\0:\0:-\0:\0";
1864 static gint emoticons_states
[] = {
1865 /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70,
1866 /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0,
1867 /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20,
1868 /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2,
1869 /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51,
1870 /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61,
1871 /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0,
1872 /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 };
1873 static const gchar
*emoticons_icon_names
[] = {
1881 "face-laugh", /* not used */
1882 "face-monkey", /* not used */
1897 typedef struct _EmoticonLoadContext
{
1898 EEmoticon
*emoticon
;
1899 EEditorPage
*editor_page
;
1900 gchar
*content_type
;
1902 } EmoticonLoadContext
;
1904 static EmoticonLoadContext
*
1905 emoticon_load_context_new (EEditorPage
*editor_page
,
1906 EEmoticon
*emoticon
)
1908 EmoticonLoadContext
*load_context
;
1910 load_context
= g_slice_new0 (EmoticonLoadContext
);
1911 load_context
->emoticon
= emoticon
;
1912 load_context
->editor_page
= editor_page
;
1914 return load_context
;
1918 emoticon_load_context_free (EmoticonLoadContext
*load_context
)
1920 g_free (load_context
->content_type
);
1921 g_free (load_context
->name
);
1922 g_slice_free (EmoticonLoadContext
, load_context
);
1926 emoticon_insert_span (EEmoticon
*emoticon
,
1927 EmoticonLoadContext
*load_context
,
1928 WebKitDOMElement
*span
)
1930 EEditorHistoryEvent
*ev
= NULL
;
1931 EEditorUndoRedoManager
*manager
;
1932 EEditorPage
*editor_page
= load_context
->editor_page
;
1933 gboolean misplaced_selection
= FALSE
, smiley_written
;
1934 gchar
*node_text
= NULL
;
1935 const gchar
*emoticon_start
;
1936 WebKitDOMDocument
*document
;
1937 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
1938 WebKitDOMNode
*node
, *insert_before
, *prev_sibling
, *next_sibling
;
1939 WebKitDOMNode
*selection_end_marker_parent
, *inserted_node
;
1940 WebKitDOMRange
*range
= NULL
;
1942 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
1944 document
= e_editor_page_get_document (editor_page
);
1945 smiley_written
= e_editor_page_get_is_smiley_written (editor_page
);
1946 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
1948 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
1949 e_editor_dom_selection_save (editor_page
);
1951 selection_start_marker
= webkit_dom_document_get_element_by_id (
1952 document
, "-x-evo-selection-start-marker");
1953 selection_end_marker
= webkit_dom_document_get_element_by_id (
1954 document
, "-x-evo-selection-end-marker");
1956 if (!smiley_written
) {
1957 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
1958 ev
= g_new0 (EEditorHistoryEvent
, 1);
1959 if (e_editor_page_get_unicode_smileys_enabled (editor_page
))
1960 ev
->type
= HISTORY_INPUT
;
1962 ev
->type
= HISTORY_SMILEY
;
1964 e_editor_dom_selection_get_coordinates (editor_page
,
1965 &ev
->before
.start
.x
,
1966 &ev
->before
.start
.y
,
1973 WebKitDOMRange
*tmp_range
= NULL
;
1975 tmp_range
= e_editor_dom_get_current_range (editor_page
);
1976 insert_delete_event (editor_page
, tmp_range
);
1977 g_clear_object (&tmp_range
);
1979 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
1981 if (!smiley_written
) {
1982 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
1983 ev
= g_new0 (EEditorHistoryEvent
, 1);
1985 if (e_editor_page_get_unicode_smileys_enabled (editor_page
))
1986 ev
->type
= HISTORY_INPUT
;
1988 ev
->type
= HISTORY_SMILEY
;
1990 e_editor_dom_selection_get_coordinates (editor_page
,
1991 &ev
->before
.start
.x
,
1992 &ev
->before
.start
.y
,
1999 e_editor_dom_selection_save (editor_page
);
2001 selection_start_marker
= webkit_dom_document_get_element_by_id (
2002 document
, "-x-evo-selection-start-marker");
2003 selection_end_marker
= webkit_dom_document_get_element_by_id (
2004 document
, "-x-evo-selection-end-marker");
2007 /* If the selection was not saved, move it into the first child of body */
2008 if (!selection_start_marker
|| !selection_end_marker
) {
2009 WebKitDOMHTMLElement
*body
;
2010 WebKitDOMNode
*child
;
2012 body
= webkit_dom_document_get_body (document
);
2013 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
2015 dom_add_selection_markers_into_element_start (
2017 WEBKIT_DOM_ELEMENT (child
),
2018 &selection_start_marker
,
2019 &selection_end_marker
);
2021 if (ev
&& !e_editor_page_get_unicode_smileys_enabled (editor_page
))
2022 e_editor_dom_selection_get_coordinates (editor_page
,
2023 &ev
->before
.start
.x
,
2024 &ev
->before
.start
.y
,
2029 /* Sometimes selection end marker is in body. Move it into next sibling */
2030 selection_end_marker_parent
= e_editor_dom_get_parent_block_node_from_child (
2031 WEBKIT_DOM_NODE (selection_end_marker
));
2032 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (selection_end_marker_parent
)) {
2033 webkit_dom_node_insert_before (
2034 webkit_dom_node_get_parent_node (
2035 WEBKIT_DOM_NODE (selection_start_marker
)),
2036 WEBKIT_DOM_NODE (selection_end_marker
),
2037 WEBKIT_DOM_NODE (selection_start_marker
),
2039 if (ev
&& !e_editor_page_get_unicode_smileys_enabled (editor_page
))
2040 e_editor_dom_selection_get_coordinates (editor_page
,
2041 &ev
->before
.start
.x
,
2042 &ev
->before
.start
.y
,
2046 selection_end_marker_parent
= webkit_dom_node_get_parent_node (
2047 WEBKIT_DOM_NODE (selection_end_marker
));
2049 /* Determine before what node we have to insert the smiley */
2050 insert_before
= WEBKIT_DOM_NODE (selection_start_marker
);
2051 prev_sibling
= webkit_dom_node_get_previous_sibling (
2052 WEBKIT_DOM_NODE (selection_start_marker
));
2054 if (webkit_dom_node_is_same_node (
2055 prev_sibling
, WEBKIT_DOM_NODE (selection_end_marker
))) {
2056 insert_before
= WEBKIT_DOM_NODE (selection_end_marker
);
2058 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
2060 webkit_dom_node_is_same_node (
2061 prev_sibling
, WEBKIT_DOM_NODE (selection_end_marker
))) {
2062 insert_before
= WEBKIT_DOM_NODE (selection_end_marker
);
2066 insert_before
= WEBKIT_DOM_NODE (selection_start_marker
);
2068 /* Look if selection is misplaced - that means that the selection was
2069 * restored before the previously inserted smiley in situations when we
2070 * are writing more smileys in a row */
2071 next_sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
));
2072 if (next_sibling
&& WEBKIT_DOM_IS_ELEMENT (next_sibling
))
2073 if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling
), "-x-evo-smiley-wrapper"))
2074 misplaced_selection
= TRUE
;
2076 range
= e_editor_dom_get_current_range (editor_page
);
2077 node
= webkit_dom_range_get_end_container (range
, NULL
);
2078 g_clear_object (&range
);
2079 if (WEBKIT_DOM_IS_TEXT (node
))
2080 node_text
= webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node
));
2082 if (misplaced_selection
) {
2083 /* Insert smiley and selection markers after it */
2084 webkit_dom_node_insert_before (
2085 webkit_dom_node_get_parent_node (insert_before
),
2086 WEBKIT_DOM_NODE (selection_start_marker
),
2087 webkit_dom_node_get_next_sibling (next_sibling
),
2089 webkit_dom_node_insert_before (
2090 webkit_dom_node_get_parent_node (insert_before
),
2091 WEBKIT_DOM_NODE (selection_end_marker
),
2092 webkit_dom_node_get_next_sibling (next_sibling
),
2094 if (e_editor_page_get_unicode_smileys_enabled (editor_page
))
2095 inserted_node
= webkit_dom_node_insert_before (
2096 webkit_dom_node_get_parent_node (insert_before
),
2097 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span
)),
2098 webkit_dom_node_get_next_sibling (next_sibling
),
2101 inserted_node
= webkit_dom_node_insert_before (
2102 webkit_dom_node_get_parent_node (insert_before
),
2103 WEBKIT_DOM_NODE (span
),
2104 webkit_dom_node_get_next_sibling (next_sibling
),
2107 if (e_editor_page_get_unicode_smileys_enabled (editor_page
))
2108 inserted_node
= webkit_dom_node_insert_before (
2109 webkit_dom_node_get_parent_node (insert_before
),
2110 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span
)),
2114 inserted_node
= webkit_dom_node_insert_before (
2115 webkit_dom_node_get_parent_node (insert_before
),
2116 WEBKIT_DOM_NODE (span
),
2121 if (!e_editor_page_get_unicode_smileys_enabled (editor_page
)) {
2122 /* ​ == UNICODE_ZERO_WIDTH_SPACE */
2123 webkit_dom_element_insert_adjacent_html (
2124 WEBKIT_DOM_ELEMENT (span
), "afterend", "​", NULL
);
2128 WebKitDOMDocumentFragment
*fragment
;
2129 WebKitDOMNode
*node
;
2131 fragment
= webkit_dom_document_create_document_fragment (document
);
2132 node
= webkit_dom_node_append_child (
2133 WEBKIT_DOM_NODE (fragment
),
2134 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (inserted_node
), TRUE
, NULL
),
2136 if (e_editor_page_get_unicode_smileys_enabled (editor_page
)) {
2137 webkit_dom_node_append_child (
2138 WEBKIT_DOM_NODE (fragment
),
2140 dom_create_selection_marker (document
, TRUE
)),
2142 webkit_dom_node_append_child (
2143 WEBKIT_DOM_NODE (fragment
),
2145 dom_create_selection_marker (document
, FALSE
)),
2148 webkit_dom_element_insert_adjacent_html (
2149 WEBKIT_DOM_ELEMENT (node
), "afterend", "​", NULL
);
2150 ev
->data
.fragment
= g_object_ref (fragment
);
2153 /* Remove the text that represents the text version of smiley that was
2154 * written into the composer. */
2155 if (node_text
&& smiley_written
) {
2156 emoticon_start
= g_utf8_strrchr (
2157 node_text
, -1, g_utf8_get_char (emoticon
->text_face
));
2158 /* Check if the written smiley is really the one that we inserted. */
2159 if (emoticon_start
) {
2160 /* The written smiley is the same as text version. */
2161 if (g_str_has_prefix (emoticon_start
, emoticon
->text_face
)) {
2162 webkit_dom_character_data_delete_data (
2163 WEBKIT_DOM_CHARACTER_DATA (node
),
2164 g_utf8_strlen (node_text
, -1) - strlen (emoticon_start
),
2165 strlen (emoticon
->text_face
),
2167 } else if (strstr (emoticon
->text_face
, "-")) {
2168 gboolean same
= TRUE
, compensate
= FALSE
;
2169 gint ii
= 0, jj
= 0;
2171 /* Try to recognize smileys without the dash e.g. :). */
2172 while (emoticon_start
[ii
] && emoticon
->text_face
[jj
]) {
2173 if (emoticon_start
[ii
] == emoticon
->text_face
[jj
]) {
2174 if (emoticon
->text_face
[jj
+1] && emoticon
->text_face
[jj
+1] == '-') {
2189 webkit_dom_character_data_delete_data (
2190 WEBKIT_DOM_CHARACTER_DATA (node
),
2191 g_utf8_strlen (node_text
, -1) - strlen (emoticon_start
),
2195 /* If we recognize smiley without dash, but we inserted
2196 * the text version with dash we need it insert new
2197 * history input event with that dash. */
2199 e_editor_undo_redo_manager_insert_dash_history_event (manager
);
2203 e_editor_page_set_is_smiley_written (editor_page
, FALSE
);
2207 e_editor_dom_selection_get_coordinates (editor_page
,
2212 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
2215 e_editor_dom_selection_restore (editor_page
);
2217 e_editor_page_emit_content_changed (editor_page
);
2223 emoticon_read_async_cb (GFile
*file
,
2224 GAsyncResult
*result
,
2225 EmoticonLoadContext
*load_context
)
2227 EEmoticon
*emoticon
= load_context
->emoticon
;
2228 EEditorPage
*editor_page
= load_context
->editor_page
;
2229 GError
*error
= NULL
;
2232 gchar
*base64_encoded
, *output
, *data
;
2233 GFileInputStream
*input_stream
;
2234 GOutputStream
*output_stream
;
2236 WebKitDOMElement
*wrapper
, *image
, *smiley_text
;
2237 WebKitDOMDocument
*document
;
2239 input_stream
= g_file_read_finish (file
, result
, &error
);
2240 g_return_if_fail (!error
&& input_stream
);
2242 output_stream
= g_memory_output_stream_new (NULL
, 0, g_realloc
, g_free
);
2244 size
= g_output_stream_splice (
2245 output_stream
, G_INPUT_STREAM (input_stream
),
2246 G_OUTPUT_STREAM_SPLICE_NONE
, NULL
, &error
);
2248 if (error
|| (size
== -1))
2251 mime_type
= g_content_type_get_mime_type (load_context
->content_type
);
2253 data
= g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream
));
2254 base64_encoded
= g_base64_encode ((const guchar
*) data
, size
);
2255 output
= g_strconcat ("data:", mime_type
, ";base64,", base64_encoded
, NULL
);
2257 html_mode
= e_editor_page_get_html_mode (editor_page
);
2258 document
= e_editor_page_get_document (editor_page
);
2260 /* Insert span with image representation and another one with text
2261 * representation and hide/show them dependant on active composer mode */
2262 wrapper
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
2264 webkit_dom_element_set_attribute (
2265 wrapper
, "class", "-x-evo-smiley-wrapper -x-evo-resizable-wrapper", NULL
);
2267 webkit_dom_element_set_attribute (
2268 wrapper
, "class", "-x-evo-smiley-wrapper", NULL
);
2270 image
= webkit_dom_document_create_element (document
, "IMG", NULL
);
2271 webkit_dom_element_set_attribute (image
, "src", output
, NULL
);
2272 webkit_dom_element_set_attribute (image
, "data-inline", "", NULL
);
2273 webkit_dom_element_set_attribute (image
, "data-name", load_context
->name
, NULL
);
2274 webkit_dom_element_set_attribute (image
, "alt", emoticon
->text_face
, NULL
);
2275 webkit_dom_element_set_attribute (image
, "class", "-x-evo-smiley-img", NULL
);
2276 webkit_dom_node_append_child (
2277 WEBKIT_DOM_NODE (wrapper
), WEBKIT_DOM_NODE (image
), NULL
);
2279 smiley_text
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
2280 webkit_dom_element_set_attribute (smiley_text
, "class", "-x-evo-smiley-text", NULL
);
2281 webkit_dom_html_element_set_inner_text (
2282 WEBKIT_DOM_HTML_ELEMENT (smiley_text
), emoticon
->text_face
, NULL
);
2283 webkit_dom_node_append_child (
2284 WEBKIT_DOM_NODE (wrapper
), WEBKIT_DOM_NODE (smiley_text
), NULL
);
2286 emoticon_insert_span (emoticon
, load_context
, wrapper
);
2288 g_free (base64_encoded
);
2291 g_object_unref (output_stream
);
2293 emoticon_load_context_free (load_context
);
2297 emoticon_query_info_async_cb (GFile
*file
,
2298 GAsyncResult
*result
,
2299 EmoticonLoadContext
*load_context
)
2301 GError
*error
= NULL
;
2304 info
= g_file_query_info_finish (file
, result
, &error
);
2305 g_return_if_fail (!error
&& info
);
2307 load_context
->content_type
= g_strdup (g_file_info_get_content_type (info
));
2308 load_context
->name
= g_strdup (g_file_info_get_name (info
));
2311 file
, G_PRIORITY_DEFAULT
, NULL
,
2312 (GAsyncReadyCallback
) emoticon_read_async_cb
, load_context
);
2314 g_object_unref (info
);
2318 e_editor_dom_insert_smiley (EEditorPage
*editor_page
,
2319 EEmoticon
*emoticon
)
2321 WebKitDOMDocument
*document
;
2323 gchar
*filename_uri
;
2324 EmoticonLoadContext
*load_context
;
2326 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
2328 document
= e_editor_page_get_document (editor_page
);
2330 if (e_editor_page_get_unicode_smileys_enabled (editor_page
)) {
2331 WebKitDOMElement
*wrapper
;
2333 wrapper
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
2334 webkit_dom_html_element_set_inner_text (
2335 WEBKIT_DOM_HTML_ELEMENT (wrapper
), emoticon
->unicode_character
, NULL
);
2337 load_context
= emoticon_load_context_new (editor_page
, emoticon
);
2338 emoticon_insert_span (emoticon
, load_context
, wrapper
);
2339 emoticon_load_context_free (load_context
);
2341 filename_uri
= e_emoticon_get_uri (emoticon
);
2342 g_return_if_fail (filename_uri
!= NULL
);
2344 load_context
= emoticon_load_context_new (editor_page
, emoticon
);
2346 file
= g_file_new_for_uri (filename_uri
);
2347 g_file_query_info_async (
2348 file
, "standard::*", G_FILE_QUERY_INFO_NONE
,
2349 G_PRIORITY_DEFAULT
, NULL
,
2350 (GAsyncReadyCallback
) emoticon_query_info_async_cb
, load_context
);
2352 g_free (filename_uri
);
2353 g_object_unref (file
);
2358 e_editor_dom_insert_smiley_by_name (EEditorPage
*editor_page
,
2361 const EEmoticon
*emoticon
;
2363 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
2365 emoticon
= e_emoticon_chooser_lookup_emoticon (name
);
2366 e_editor_page_set_is_smiley_written (editor_page
, FALSE
);
2367 e_editor_dom_insert_smiley (editor_page
, (EEmoticon
*) emoticon
);
2371 e_editor_dom_check_magic_smileys (EEditorPage
*editor_page
)
2373 WebKitDOMNode
*node
;
2374 WebKitDOMRange
*range
= NULL
;
2375 gint pos
, state
, relative
, start
;
2379 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
2381 if (!e_editor_page_get_magic_smileys_enabled (editor_page
))
2384 range
= e_editor_dom_get_current_range (editor_page
);
2385 node
= webkit_dom_range_get_end_container (range
, NULL
);
2386 if (!WEBKIT_DOM_IS_TEXT (node
)) {
2387 g_clear_object (&range
);
2391 node_text
= webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node
));
2392 if (node_text
== NULL
) {
2393 g_clear_object (&range
);
2397 start
= webkit_dom_range_get_end_offset (range
, NULL
) - 1;
2401 uc
= g_utf8_get_char (g_utf8_offset_to_pointer (node_text
, pos
));
2403 while (emoticons_chars
[state
+ relative
]) {
2404 if (emoticons_chars
[state
+ relative
] == uc
)
2408 state
= emoticons_states
[state
+ relative
];
2409 /* 0 .. not found, -n .. found n-th */
2415 /* Special case needed to recognize angel and devilish. */
2416 if (pos
> 0 && state
== -14) {
2417 uc
= g_utf8_get_char (g_utf8_offset_to_pointer (node_text
, pos
- 1));
2421 } else if (uc
== '>') {
2428 const EEmoticon
*emoticon
;
2431 uc
= g_utf8_get_char (g_utf8_offset_to_pointer (node_text
, pos
- 1));
2432 if (!g_unichar_isspace (uc
)) {
2434 g_clear_object (&range
);
2439 emoticon
= e_emoticon_chooser_lookup_emoticon (
2440 emoticons_icon_names
[-state
- 1]);
2441 e_editor_page_set_is_smiley_written (editor_page
, TRUE
);
2442 e_editor_dom_insert_smiley (editor_page
, (EEmoticon
*) emoticon
);
2445 g_clear_object (&range
);
2450 dom_set_links_active (WebKitDOMDocument
*document
,
2453 WebKitDOMElement
*style
;
2455 style
= webkit_dom_document_get_element_by_id (document
, "-x-evo-style-a");
2457 remove_node (WEBKIT_DOM_NODE (style
));
2460 WebKitDOMHTMLHeadElement
*head
;
2461 head
= webkit_dom_document_get_head (document
);
2463 style
= webkit_dom_document_create_element (document
, "STYLE", NULL
);
2464 webkit_dom_element_set_id (style
, "-x-evo-style-a");
2465 webkit_dom_element_set_attribute (style
, "type", "text/css", NULL
);
2466 webkit_dom_html_element_set_inner_text (
2467 WEBKIT_DOM_HTML_ELEMENT (style
), "a { cursor: text; }", NULL
);
2469 webkit_dom_node_append_child (
2470 WEBKIT_DOM_NODE (head
), WEBKIT_DOM_NODE (style
), NULL
);
2475 fix_paragraph_structure_after_pressing_enter_after_smiley (WebKitDOMDocument
*document
)
2477 WebKitDOMElement
*element
;
2479 element
= webkit_dom_document_query_selector (
2480 document
, "span.-x-evo-smiley-wrapper > br", NULL
);
2483 WebKitDOMNode
*parent
;
2485 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
2486 webkit_dom_element_set_inner_html (
2487 webkit_dom_node_get_parent_element (parent
),
2488 UNICODE_ZERO_WIDTH_SPACE
,
2494 fix_paragraph_structure_after_pressing_enter (EEditorPage
*editor_page
)
2496 WebKitDOMDocument
*document
;
2497 WebKitDOMNode
*body
, *prev_sibling
, *node
;
2498 WebKitDOMElement
*br
;
2499 gboolean prev_is_heading
= FALSE
;
2501 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
2503 document
= e_editor_page_get_document (editor_page
);
2504 body
= WEBKIT_DOM_NODE (webkit_dom_document_get_body (document
));
2506 e_editor_dom_selection_save (editor_page
);
2508 /* When pressing Enter on empty line in the list (or after heading elements)
2509 * WebKit will end that list and inserts <div><br></div> so replace it
2510 * with the right paragraph element. */
2511 br
= webkit_dom_document_query_selector (
2512 document
, "body > div:not([data-evo-paragraph]) > #-x-evo-selection-end-marker + br", NULL
);
2514 if (!br
|| webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (br
)) ||
2515 webkit_dom_node_get_previous_sibling (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (br
))))
2518 node
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (br
));
2520 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
2521 if (prev_sibling
&& WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (prev_sibling
))
2522 prev_is_heading
= TRUE
;
2524 webkit_dom_node_replace_child (
2526 WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page
, FALSE
)),
2531 e_editor_dom_selection_restore (editor_page
);
2533 return prev_is_heading
;
2537 surround_text_with_paragraph_if_needed (EEditorPage
*editor_page
,
2538 WebKitDOMNode
*node
)
2540 WebKitDOMNode
*next_sibling
= webkit_dom_node_get_next_sibling (node
);
2541 WebKitDOMNode
*prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
2542 WebKitDOMNode
*parent
= webkit_dom_node_get_parent_node (node
);
2543 WebKitDOMElement
*element
;
2545 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
2547 /* All text in composer has to be written in div elements, so if
2548 * we are writing something straight to the body, surround it with
2550 if (WEBKIT_DOM_IS_TEXT (node
) &&
2551 (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
) ||
2552 WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent
))) {
2553 element
= e_editor_dom_put_node_into_paragraph (editor_page
, node
, TRUE
);
2554 if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent
))
2555 webkit_dom_element_remove_attribute (element
, "style");
2557 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
))
2558 remove_node (next_sibling
);
2561 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
) &&
2562 element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling
), "Apple-tab-span")) {
2563 webkit_dom_node_insert_before (
2564 WEBKIT_DOM_NODE (element
),
2566 webkit_dom_node_get_first_child (
2567 WEBKIT_DOM_NODE (element
)),
2578 selection_is_in_table (WebKitDOMDocument
*document
,
2579 gboolean
*first_cell
,
2580 WebKitDOMNode
**table_node
)
2582 WebKitDOMDOMWindow
*dom_window
= NULL
;
2583 WebKitDOMDOMSelection
*dom_selection
= NULL
;
2584 WebKitDOMNode
*node
, *parent
;
2585 WebKitDOMRange
*range
= NULL
;
2587 dom_window
= webkit_dom_document_get_default_view (document
);
2588 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
2589 g_clear_object (&dom_window
);
2591 if (first_cell
!= NULL
)
2592 *first_cell
= FALSE
;
2594 if (table_node
!= NULL
)
2597 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
2598 g_clear_object (&dom_selection
);
2602 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
2603 node
= webkit_dom_range_get_start_container (range
, NULL
);
2604 g_clear_object (&dom_selection
);
2607 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
2608 if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent
)) {
2609 if (first_cell
!= NULL
) {
2610 if (!webkit_dom_node_get_previous_sibling (parent
)) {
2611 gboolean on_start
= TRUE
;
2614 tmp
= webkit_dom_node_get_previous_sibling (node
);
2615 if (!tmp
&& WEBKIT_DOM_IS_TEXT (node
))
2616 on_start
= webkit_dom_range_get_start_offset (range
, NULL
) == 0;
2621 node
= webkit_dom_node_get_parent_node (parent
);
2622 if (node
&& WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (node
))
2623 if (!webkit_dom_node_get_previous_sibling (node
))
2628 g_clear_object (&range
);
2632 if (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (parent
)) {
2633 if (table_node
!= NULL
)
2634 *table_node
= parent
;
2636 g_clear_object (&range
);
2640 parent
= webkit_dom_node_get_parent_node (parent
);
2643 g_clear_object (&range
);
2645 if (table_node
== NULL
)
2648 return *table_node
!= NULL
;
2652 jump_to_next_table_cell (WebKitDOMDocument
*document
,
2655 WebKitDOMDOMWindow
*dom_window
= NULL
;
2656 WebKitDOMDOMSelection
*dom_selection
= NULL
;
2657 WebKitDOMNode
*node
, *cell
;
2658 WebKitDOMRange
*range
= NULL
;
2660 if (!selection_is_in_table (document
, NULL
, NULL
))
2663 dom_window
= webkit_dom_document_get_default_view (document
);
2664 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
2665 g_clear_object (&dom_window
);
2666 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
2667 node
= webkit_dom_range_get_start_container (range
, NULL
);
2670 while (cell
&& !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell
)) {
2671 cell
= webkit_dom_node_get_parent_node (cell
);
2674 if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell
)) {
2675 g_clear_object (&range
);
2676 g_clear_object (&dom_selection
);
2681 /* Get previous cell */
2682 node
= webkit_dom_node_get_previous_sibling (cell
);
2683 if (!node
|| !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node
)) {
2684 /* No cell, go one row up. */
2685 node
= webkit_dom_node_get_parent_node (cell
);
2686 node
= webkit_dom_node_get_previous_sibling (node
);
2687 if (node
&& WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node
)) {
2688 node
= webkit_dom_node_get_last_child (node
);
2690 /* No row above, move to the block before table. */
2691 node
= webkit_dom_node_get_parent_node (cell
);
2692 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node
)))
2693 node
= webkit_dom_node_get_parent_node (node
);
2695 node
= webkit_dom_node_get_previous_sibling (node
);
2700 node
= webkit_dom_node_get_next_sibling (cell
);
2701 if (!node
|| !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node
)) {
2702 /* No cell, go one row below. */
2703 node
= webkit_dom_node_get_parent_node (cell
);
2704 node
= webkit_dom_node_get_next_sibling (node
);
2705 if (node
&& WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node
)) {
2706 node
= webkit_dom_node_get_first_child (node
);
2708 /* No row below, move to the block after table. */
2709 node
= webkit_dom_node_get_parent_node (cell
);
2710 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node
)))
2711 node
= webkit_dom_node_get_parent_node (node
);
2713 node
= webkit_dom_node_get_next_sibling (node
);
2719 g_clear_object (&range
);
2720 g_clear_object (&dom_selection
);
2724 webkit_dom_range_select_node_contents (range
, node
, NULL
);
2725 webkit_dom_range_collapse (range
, TRUE
, NULL
);
2726 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
2727 webkit_dom_dom_selection_add_range (dom_selection
, range
);
2728 g_clear_object (&range
);
2729 g_clear_object (&dom_selection
);
2735 save_history_before_event_in_table (EEditorPage
*editor_page
,
2736 WebKitDOMRange
*range
)
2738 WebKitDOMNode
*node
;
2739 WebKitDOMElement
*block
;
2741 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
2743 node
= webkit_dom_range_get_start_container (range
, NULL
);
2744 if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node
))
2745 block
= WEBKIT_DOM_ELEMENT (node
);
2747 block
= get_parent_block_element (node
);
2749 if (block
&& WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (block
)) {
2750 EEditorUndoRedoManager
*manager
;
2751 EEditorHistoryEvent
*ev
;
2753 ev
= g_new0 (EEditorHistoryEvent
, 1);
2754 ev
->type
= HISTORY_TABLE_INPUT
;
2756 e_editor_dom_selection_save (editor_page
);
2757 ev
->data
.dom
.from
= g_object_ref (webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block
), TRUE
, NULL
));
2758 e_editor_dom_selection_restore (editor_page
);
2760 e_editor_dom_selection_get_coordinates (editor_page
,
2761 &ev
->before
.start
.x
,
2762 &ev
->before
.start
.y
,
2766 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
2767 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
2776 insert_tabulator (EEditorPage
*editor_page
)
2778 EEditorUndoRedoManager
*manager
;
2779 EEditorHistoryEvent
*ev
= NULL
;
2782 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
2784 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
2786 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
2787 ev
= g_new0 (EEditorHistoryEvent
, 1);
2788 ev
->type
= HISTORY_INPUT
;
2790 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
2791 WebKitDOMRange
*tmp_range
= NULL
;
2793 tmp_range
= e_editor_dom_get_current_range (editor_page
);
2794 insert_delete_event (editor_page
, tmp_range
);
2795 g_clear_object (&tmp_range
);
2798 e_editor_dom_selection_get_coordinates (editor_page
,
2799 &ev
->before
.start
.x
,
2800 &ev
->before
.start
.y
,
2804 ev
->before
.end
.x
= ev
->before
.start
.x
;
2805 ev
->before
.end
.y
= ev
->before
.start
.y
;
2808 success
= e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT
, "\t");
2812 WebKitDOMDocument
*document
;
2813 WebKitDOMElement
*element
;
2814 WebKitDOMDocumentFragment
*fragment
;
2816 document
= e_editor_page_get_document (editor_page
);
2818 e_editor_dom_selection_get_coordinates (editor_page
,
2824 fragment
= webkit_dom_document_create_document_fragment (document
);
2825 element
= webkit_dom_document_create_element (document
, "span", NULL
);
2826 webkit_dom_html_element_set_inner_text (
2827 WEBKIT_DOM_HTML_ELEMENT (element
), "\t", NULL
);
2828 webkit_dom_element_set_attribute (
2829 element
, "class", "Apple-tab-span", NULL
);
2830 webkit_dom_element_set_attribute (
2831 element
, "style", "white-space:pre", NULL
);
2832 webkit_dom_node_append_child (
2833 WEBKIT_DOM_NODE (fragment
), WEBKIT_DOM_NODE (element
), NULL
);
2834 webkit_dom_node_append_child (
2835 WEBKIT_DOM_NODE (fragment
),
2836 WEBKIT_DOM_NODE (dom_create_selection_marker (document
, TRUE
)),
2838 webkit_dom_node_append_child (
2839 WEBKIT_DOM_NODE (fragment
),
2840 WEBKIT_DOM_NODE (dom_create_selection_marker (document
, FALSE
)),
2842 ev
->data
.fragment
= g_object_ref (fragment
);
2844 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
2845 e_editor_page_emit_content_changed (editor_page
);
2847 e_editor_undo_redo_manager_remove_current_history_event (manager
);
2848 e_editor_undo_redo_manager_remove_current_history_event (manager
);
2857 body_keypress_event_cb (WebKitDOMElement
*element
,
2858 WebKitDOMUIEvent
*event
,
2859 EEditorPage
*editor_page
)
2861 WebKitDOMDocument
*document
;
2862 WebKitDOMDOMWindow
*dom_window
= NULL
;
2863 WebKitDOMDOMSelection
*dom_selection
= NULL
;
2864 WebKitDOMRange
*range
= NULL
;
2866 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
2868 e_editor_page_set_is_processing_keypress_event (editor_page
, TRUE
);
2870 document
= webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element
));
2871 dom_window
= webkit_dom_document_get_default_view (document
);
2872 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
2873 g_clear_object (&dom_window
);
2874 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
2876 if (range
&& !webkit_dom_range_get_collapsed (range
, NULL
))
2877 insert_delete_event (editor_page
, range
);
2879 g_clear_object (&dom_selection
);
2880 g_clear_object (&range
);
2884 body_keydown_event_cb (WebKitDOMElement
*element
,
2885 WebKitDOMUIEvent
*event
,
2886 EEditorPage
*editor_page
)
2888 gboolean backspace_key
, delete_key
, space_key
, return_key
;
2889 gboolean shift_key
, control_key
, tabulator_key
;
2891 WebKitDOMDocument
*document
;
2892 WebKitDOMDOMWindow
*dom_window
= NULL
;
2893 WebKitDOMDOMSelection
*dom_selection
= NULL
;
2894 WebKitDOMRange
*range
= NULL
;
2896 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
2898 document
= webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element
));
2900 key_code
= webkit_dom_ui_event_get_key_code (event
);
2901 delete_key
= key_code
== HTML_KEY_CODE_DELETE
;
2902 return_key
= key_code
== HTML_KEY_CODE_RETURN
;
2903 backspace_key
= key_code
== HTML_KEY_CODE_BACKSPACE
;
2904 space_key
= key_code
== HTML_KEY_CODE_SPACE
;
2905 tabulator_key
= key_code
== HTML_KEY_CODE_TABULATOR
;
2907 if (key_code
== HTML_KEY_CODE_CONTROL
) {
2908 dom_set_links_active (document
, TRUE
);
2912 e_editor_page_set_dont_save_history_in_body_input (editor_page
, delete_key
|| backspace_key
);
2914 e_editor_page_set_return_key_pressed (editor_page
, return_key
);
2915 e_editor_page_set_space_key_pressed (editor_page
, space_key
);
2917 if (!(delete_key
|| return_key
|| backspace_key
|| space_key
|| tabulator_key
))
2920 shift_key
= webkit_dom_keyboard_event_get_shift_key (WEBKIT_DOM_KEYBOARD_EVENT (event
));
2921 control_key
= webkit_dom_keyboard_event_get_ctrl_key (WEBKIT_DOM_KEYBOARD_EVENT (event
));
2923 if (tabulator_key
) {
2924 if (jump_to_next_table_cell (document
, shift_key
)) {
2925 webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event
));
2929 if (!shift_key
&& insert_tabulator (editor_page
))
2930 webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event
));
2935 if (return_key
&& e_editor_dom_key_press_event_process_return_key (editor_page
)) {
2936 webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event
));
2940 if (backspace_key
&& e_editor_dom_key_press_event_process_backspace_key (editor_page
)) {
2941 webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event
));
2945 if (delete_key
|| backspace_key
) {
2946 if (e_editor_dom_key_press_event_process_delete_or_backspace_key (editor_page
, key_code
, control_key
, delete_key
))
2947 webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event
));
2951 dom_window
= webkit_dom_document_get_default_view (document
);
2952 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
2953 g_clear_object (&dom_window
);
2954 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
2956 if (save_history_before_event_in_table (editor_page
, range
))
2960 EEditorHistoryEvent
*ev
;
2961 EEditorUndoRedoManager
*manager
;
2963 /* Insert new history event for Return to have the right coordinates.
2964 * The fragment will be added later. */
2965 ev
= g_new0 (EEditorHistoryEvent
, 1);
2966 ev
->type
= HISTORY_INPUT
;
2968 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
2970 e_editor_dom_selection_get_coordinates (editor_page
,
2971 &ev
->before
.start
.x
,
2972 &ev
->before
.start
.y
,
2975 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
2978 g_clear_object (&range
);
2979 g_clear_object (&dom_selection
);
2983 save_history_after_event_in_table (EEditorPage
*editor_page
)
2985 WebKitDOMDocument
*document
;
2986 WebKitDOMDOMWindow
*dom_window
= NULL
;
2987 WebKitDOMDOMSelection
*dom_selection
= NULL
;
2988 WebKitDOMElement
*element
;
2989 WebKitDOMNode
*node
;
2990 WebKitDOMRange
*range
= NULL
;
2991 EEditorHistoryEvent
*ev
;
2992 EEditorUndoRedoManager
*manager
;
2994 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
2996 document
= e_editor_page_get_document (editor_page
);
2997 dom_window
= webkit_dom_document_get_default_view (document
);
2998 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
2999 g_clear_object (&dom_window
);
3001 if (!webkit_dom_dom_selection_get_range_count (dom_selection
)) {
3002 g_clear_object (&dom_selection
);
3005 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
3007 /* Find if writing into table. */
3008 node
= webkit_dom_range_get_start_container (range
, NULL
);
3009 if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node
))
3010 element
= WEBKIT_DOM_ELEMENT (node
);
3012 element
= get_parent_block_element (node
);
3014 g_clear_object (&dom_selection
);
3015 g_clear_object (&range
);
3017 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
3018 /* If writing to table we have to create different history event. */
3019 if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element
)) {
3020 ev
= e_editor_undo_redo_manager_get_current_history_event (manager
);
3021 if (ev
->type
!= HISTORY_TABLE_INPUT
)
3026 e_editor_dom_selection_save (editor_page
);
3028 e_editor_dom_selection_get_coordinates (editor_page
,
3034 ev
->data
.dom
.to
= g_object_ref (webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element
), TRUE
, NULL
));
3036 e_editor_dom_selection_restore (editor_page
);
3042 save_history_for_input (EEditorPage
*editor_page
)
3044 WebKitDOMDocument
*document
;
3045 WebKitDOMDocumentFragment
*fragment
;
3046 WebKitDOMDOMWindow
*dom_window
= NULL
;
3047 WebKitDOMDOMSelection
*dom_selection
= NULL
;
3048 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
3049 WebKitDOMNode
*start_container
;
3050 EEditorHistoryEvent
*ev
;
3051 EEditorUndoRedoManager
*manager
;
3054 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3056 document
= e_editor_page_get_document (editor_page
);
3057 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
3058 dom_window
= webkit_dom_document_get_default_view (document
);
3059 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
3060 g_clear_object (&dom_window
);
3062 if (!webkit_dom_dom_selection_get_range_count (dom_selection
)) {
3063 g_clear_object (&dom_selection
);
3067 if (e_editor_page_get_return_key_pressed (editor_page
)) {
3068 ev
= e_editor_undo_redo_manager_get_current_history_event (manager
);
3069 if (ev
->type
!= HISTORY_INPUT
) {
3070 g_clear_object (&dom_selection
);
3074 ev
= g_new0 (EEditorHistoryEvent
, 1);
3075 ev
->type
= HISTORY_INPUT
;
3078 e_editor_page_block_selection_changed (editor_page
);
3080 e_editor_dom_selection_get_coordinates (editor_page
,
3086 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
3087 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
3088 offset
= webkit_dom_range_get_start_offset (range_clone
, NULL
);
3089 start_container
= webkit_dom_range_get_start_container (range_clone
, NULL
);
3091 webkit_dom_range_set_start (
3096 fragment
= webkit_dom_range_clone_contents (range_clone
, NULL
);
3097 /* We have to specially handle Return key press */
3098 if (e_editor_page_get_return_key_pressed (editor_page
)) {
3099 WebKitDOMElement
*element_start
, *element_end
;
3100 WebKitDOMNode
*parent_start
, *parent_end
, *node
;
3102 element_start
= webkit_dom_document_create_element (document
, "span", NULL
);
3103 webkit_dom_range_surround_contents (range
, WEBKIT_DOM_NODE (element_start
), NULL
);
3104 webkit_dom_dom_selection_modify (dom_selection
, "move", "left", "character");
3105 g_clear_object (&range
);
3106 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
3107 element_end
= webkit_dom_document_create_element (document
, "span", NULL
);
3108 webkit_dom_range_surround_contents (range
, WEBKIT_DOM_NODE (element_end
), NULL
);
3110 parent_start
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_start
));
3111 parent_end
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_end
));
3113 while (parent_start
&& parent_end
&& !webkit_dom_node_is_same_node (parent_start
, parent_end
) &&
3114 !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_start
) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_end
)) {
3115 webkit_dom_node_insert_before (
3116 WEBKIT_DOM_NODE (fragment
),
3117 webkit_dom_node_clone_node_with_error (parent_start
, FALSE
, NULL
),
3118 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
3120 parent_start
= webkit_dom_node_get_parent_node (parent_start
);
3121 parent_end
= webkit_dom_node_get_parent_node (parent_end
);
3124 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
3125 while (webkit_dom_node_get_next_sibling (node
)) {
3126 WebKitDOMNode
*last_child
;
3128 last_child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment
));
3129 webkit_dom_node_append_child (
3130 webkit_dom_node_get_previous_sibling (last_child
),
3135 node
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment
));
3136 while (webkit_dom_node_get_last_child (node
)) {
3137 node
= webkit_dom_node_get_last_child (node
);
3140 webkit_dom_node_append_child (
3143 webkit_dom_document_create_element (document
, "br", NULL
)),
3145 webkit_dom_node_append_child (
3148 dom_create_selection_marker (document
, TRUE
)),
3150 webkit_dom_node_append_child (
3153 dom_create_selection_marker (document
, FALSE
)),
3156 remove_node (WEBKIT_DOM_NODE (element_start
));
3157 remove_node (WEBKIT_DOM_NODE (element_end
));
3160 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
3162 webkit_dom_dom_selection_modify (dom_selection
, "move", "right", "character");
3164 webkit_dom_node_append_child (
3165 WEBKIT_DOM_NODE (fragment
),
3167 dom_create_selection_marker (document
, TRUE
)),
3169 webkit_dom_node_append_child (
3170 WEBKIT_DOM_NODE (fragment
),
3172 dom_create_selection_marker (document
, FALSE
)),
3176 g_clear_object (&dom_selection
);
3177 g_clear_object (&range
);
3178 g_clear_object (&range_clone
);
3180 e_editor_page_unblock_selection_changed (editor_page
);
3182 ev
->data
.fragment
= g_object_ref (fragment
);
3184 if (!e_editor_page_get_return_key_pressed (editor_page
))
3185 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
3188 typedef struct _TimeoutContext TimeoutContext
;
3190 struct _TimeoutContext
{
3191 EEditorPage
*editor_page
;
3195 timeout_context_free (TimeoutContext
*context
)
3197 g_slice_free (TimeoutContext
, context
);
3201 force_spell_check_on_timeout (TimeoutContext
*context
)
3203 e_editor_dom_force_spell_check_in_viewport (context
->editor_page
);
3204 e_editor_page_set_spell_check_on_scroll_event_source_id (context
->editor_page
, 0);
3209 body_scroll_event_cb (WebKitDOMElement
*element
,
3210 WebKitDOMEvent
*event
,
3211 EEditorPage
*editor_page
)
3213 TimeoutContext
*context
;
3216 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3218 if (!e_editor_page_get_inline_spelling_enabled (editor_page
))
3221 context
= g_slice_new0 (TimeoutContext
);
3222 context
->editor_page
= editor_page
;
3224 id
= e_editor_page_get_spell_check_on_scroll_event_source_id (editor_page
);
3226 g_source_remove (id
);
3228 id
= g_timeout_add_seconds_full (
3231 (GSourceFunc
)force_spell_check_on_timeout
,
3233 (GDestroyNotify
)timeout_context_free
);
3235 e_editor_page_set_spell_check_on_scroll_event_source_id (editor_page
, id
);
3239 remove_zero_width_spaces_on_body_input (EEditorPage
*editor_page
,
3240 WebKitDOMNode
*node
)
3244 html_mode
= e_editor_page_get_html_mode (editor_page
);
3245 /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE
3246 * to move caret into right space. When this callback is called it is not
3247 * necessary anymore so remove it */
3249 WebKitDOMElement
*parent
= webkit_dom_node_get_parent_element (node
);
3252 WebKitDOMNode
*prev_sibling
;
3254 prev_sibling
= webkit_dom_node_get_previous_sibling (
3255 WEBKIT_DOM_NODE (parent
));
3257 if (prev_sibling
&& WEBKIT_DOM_IS_TEXT (prev_sibling
)) {
3258 gchar
*text
= webkit_dom_node_get_text_content (
3261 if (g_strcmp0 (text
, UNICODE_ZERO_WIDTH_SPACE
) == 0)
3262 remove_node (prev_sibling
);
3270 /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */
3271 if (WEBKIT_DOM_IS_TEXT (node
)) {
3272 gchar
*text
= webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node
));
3273 glong length
= webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
));
3274 WebKitDOMNode
*parent
;
3276 /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
3277 * character as when we will remove it it will collapse */
3279 if (g_str_has_prefix (text
, UNICODE_ZERO_WIDTH_SPACE
))
3280 webkit_dom_character_data_replace_data (
3281 WEBKIT_DOM_CHARACTER_DATA (node
), 0, 1, "", NULL
);
3282 else if (g_str_has_suffix (text
, UNICODE_ZERO_WIDTH_SPACE
))
3283 webkit_dom_character_data_replace_data (
3284 WEBKIT_DOM_CHARACTER_DATA (node
), length
- 1, 1, "", NULL
);
3288 parent
= webkit_dom_node_get_parent_node (node
);
3289 if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent
) &&
3290 !webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "data-evo-paragraph")) {
3292 webkit_dom_element_set_attribute (
3293 WEBKIT_DOM_ELEMENT (parent
),
3294 "data-evo-paragraph",
3298 e_editor_dom_set_paragraph_style (
3299 editor_page
, WEBKIT_DOM_ELEMENT (parent
), -1, 0, NULL
);
3302 /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the
3303 * caret position to right place. It is removed when user starts typing. But
3304 * when the user will press left arrow he will move the caret into
3305 * smiley wrapper. If he will start to write there we have to move the written
3306 * text out of the wrapper and move caret to right place */
3307 if (WEBKIT_DOM_IS_ELEMENT (parent
) &&
3308 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-smiley-text")) {
3310 WebKitDOMCharacterData
*data
;
3311 WebKitDOMText
*text_node
;
3312 WebKitDOMDocument
*document
;
3314 document
= e_editor_page_get_document (editor_page
);
3316 /* Split out the newly written character to its own text node, */
3317 data
= WEBKIT_DOM_CHARACTER_DATA (node
);
3318 parent
= webkit_dom_node_get_parent_node (parent
);
3319 text
= webkit_dom_character_data_substring_data (
3321 webkit_dom_character_data_get_length (data
) - 1,
3324 webkit_dom_character_data_delete_data (
3326 webkit_dom_character_data_get_length (data
) - 1,
3329 text_node
= webkit_dom_document_create_text_node (document
, text
);
3332 webkit_dom_node_insert_before (
3333 webkit_dom_node_get_parent_node (parent
),
3335 dom_create_selection_marker (document
, FALSE
)),
3336 webkit_dom_node_get_next_sibling (parent
),
3338 webkit_dom_node_insert_before (
3339 webkit_dom_node_get_parent_node (parent
),
3341 dom_create_selection_marker (document
, TRUE
)),
3342 webkit_dom_node_get_next_sibling (parent
),
3344 /* Move the text node outside of smiley. */
3345 webkit_dom_node_insert_before (
3346 webkit_dom_node_get_parent_node (parent
),
3347 WEBKIT_DOM_NODE (text_node
),
3348 webkit_dom_node_get_next_sibling (parent
),
3350 e_editor_dom_selection_restore (editor_page
);
3356 e_editor_dom_body_input_event_process (EEditorPage
*editor_page
,
3357 WebKitDOMEvent
*event
)
3359 WebKitDOMDocument
*document
;
3360 WebKitDOMNode
*node
;
3361 WebKitDOMRange
*range
= NULL
;
3362 EEditorUndoRedoManager
*manager
;
3363 gboolean do_spell_check
= FALSE
;
3366 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3368 document
= e_editor_page_get_document (editor_page
);
3369 range
= e_editor_dom_get_current_range (editor_page
);
3370 node
= webkit_dom_range_get_end_container (range
, NULL
);
3372 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
3374 html_mode
= e_editor_page_get_html_mode (editor_page
);
3375 e_editor_page_emit_content_changed (editor_page
);
3377 if (e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
3378 e_editor_undo_redo_manager_set_operation_in_progress (manager
, FALSE
);
3379 e_editor_page_set_dont_save_history_in_body_input (editor_page
, FALSE
);
3380 remove_zero_width_spaces_on_body_input (editor_page
, node
);
3381 do_spell_check
= TRUE
;
3385 /* When the Backspace is pressed in a bulleted list item with just one
3386 * character left in it, WebKit will create another BR element in the
3389 WebKitDOMElement
*element
;
3391 element
= webkit_dom_document_query_selector (
3392 document
, "ul > li > br + br", NULL
);
3395 remove_node (WEBKIT_DOM_NODE (element
));
3398 if (!save_history_after_event_in_table (editor_page
)) {
3399 if (!e_editor_page_get_dont_save_history_in_body_input (editor_page
))
3400 save_history_for_input (editor_page
);
3402 do_spell_check
= TRUE
;
3405 /* Don't try to look for smileys if we are deleting text. */
3406 if (!e_editor_page_get_dont_save_history_in_body_input (editor_page
))
3407 e_editor_dom_check_magic_smileys (editor_page
);
3409 e_editor_page_set_dont_save_history_in_body_input (editor_page
, FALSE
);
3411 if (e_editor_page_get_return_key_pressed (editor_page
) ||
3412 e_editor_page_get_space_key_pressed (editor_page
)) {
3413 e_editor_dom_check_magic_links (editor_page
, FALSE
);
3414 if (e_editor_page_get_return_key_pressed (editor_page
)) {
3415 if (fix_paragraph_structure_after_pressing_enter (editor_page
) &&
3417 /* When the return is pressed in a H1-6 element, WebKit doesn't
3418 * continue with the same element, but creates normal paragraph,
3419 * so we have to unset the bold font. */
3420 e_editor_undo_redo_manager_set_operation_in_progress (manager
, TRUE
);
3421 e_editor_dom_selection_set_bold (editor_page
, FALSE
);
3422 e_editor_undo_redo_manager_set_operation_in_progress (manager
, FALSE
);
3425 fix_paragraph_structure_after_pressing_enter_after_smiley (document
);
3427 do_spell_check
= TRUE
;
3430 WebKitDOMNode
*node
;
3432 node
= webkit_dom_range_get_end_container (range
, NULL
);
3434 if (surround_text_with_paragraph_if_needed (editor_page
, node
)) {
3435 WebKitDOMElement
*element
;
3437 element
= webkit_dom_document_get_element_by_id (
3438 document
, "-x-evo-selection-start-marker");
3439 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
3440 e_editor_dom_selection_restore (editor_page
);
3443 if (WEBKIT_DOM_IS_TEXT (node
)) {
3444 WebKitDOMElement
*parent
;
3447 text
= webkit_dom_node_get_text_content (node
);
3449 if (text
&& *text
&& *text
!= ' ' && !g_str_has_prefix (text
, UNICODE_NBSP
)) {
3450 gboolean valid
= FALSE
;
3452 if (*text
== '?' && strlen (text
) > 1)
3454 else if (!strchr (URL_INVALID_TRAILING_CHARS
, *text
))
3458 WebKitDOMNode
*prev_sibling
;
3460 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
3462 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling
))
3463 e_editor_dom_check_magic_links (editor_page
, FALSE
);
3467 parent
= webkit_dom_node_get_parent_element (node
);
3468 if (element_has_class (parent
, "-x-evo-resizable-wrapper") ||
3469 element_has_class (parent
, "-x-evo-smiley-wrapper")) {
3470 WebKitDOMDOMWindow
*dom_window
= NULL
;
3471 WebKitDOMDOMSelection
*dom_selection
= NULL
;
3472 WebKitDOMNode
*prev_sibling
;
3473 gboolean writing_before
= TRUE
;
3475 dom_window
= webkit_dom_document_get_default_view (document
);
3476 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
3478 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
3479 if (prev_sibling
&& WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (prev_sibling
))
3480 writing_before
= FALSE
;
3482 webkit_dom_node_insert_before (
3483 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent
)),
3486 WEBKIT_DOM_NODE (parent
) :
3487 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent
)),
3490 g_clear_object (&range
);
3492 range
= webkit_dom_document_create_range (document
);
3493 webkit_dom_range_select_node_contents (range
, node
, NULL
);
3494 webkit_dom_range_collapse (range
, FALSE
, NULL
);
3496 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
3497 webkit_dom_dom_selection_add_range (dom_selection
, range
);
3499 g_clear_object (&dom_window
);
3500 g_clear_object (&dom_selection
);
3507 remove_zero_width_spaces_on_body_input (editor_page
, node
);
3509 /* Writing into quoted content */
3511 gint citation_level
;
3512 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
3513 WebKitDOMNode
*node
, *parent
;
3515 node
= webkit_dom_range_get_end_container (range
, NULL
);
3517 citation_level
= e_editor_dom_get_citation_level (node
);
3518 if (citation_level
== 0)
3521 selection_start_marker
= webkit_dom_document_get_element_by_id (
3522 document
, "-x-evo-selection-start-marker");
3523 if (selection_start_marker
)
3526 e_editor_dom_selection_save (editor_page
);
3528 selection_start_marker
= webkit_dom_document_get_element_by_id (
3529 document
, "-x-evo-selection-start-marker");
3530 selection_end_marker
= webkit_dom_document_get_element_by_id (
3531 document
, "-x-evo-selection-end-marker");
3532 /* If the selection was not saved, move it into the first child of body */
3533 if (!selection_start_marker
|| !selection_end_marker
) {
3534 WebKitDOMHTMLElement
*body
;
3535 WebKitDOMNode
*child
;
3537 body
= webkit_dom_document_get_body (document
);
3538 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
3540 dom_add_selection_markers_into_element_start (
3542 WEBKIT_DOM_ELEMENT (child
),
3543 &selection_start_marker
,
3544 &selection_end_marker
);
3547 /* We have to process elements only inside normal block */
3548 parent
= WEBKIT_DOM_NODE (get_parent_block_element (
3549 WEBKIT_DOM_NODE (selection_start_marker
)));
3550 if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent
)) {
3551 e_editor_dom_selection_restore (editor_page
);
3555 if (selection_start_marker
) {
3557 gint text_length
, word_wrap_length
, length
;
3558 WebKitDOMElement
*block
;
3559 gboolean remove_quoting
= FALSE
;
3561 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
3562 length
= word_wrap_length
- 2 * citation_level
;
3564 block
= WEBKIT_DOM_ELEMENT (parent
);
3565 if (webkit_dom_element_query_selector (
3566 WEBKIT_DOM_ELEMENT (block
), ".-x-evo-quoted", NULL
)) {
3567 WebKitDOMNode
*prev_sibling
;
3569 prev_sibling
= webkit_dom_node_get_previous_sibling (
3570 WEBKIT_DOM_NODE (selection_end_marker
));
3572 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
))
3573 remove_quoting
= element_has_class (
3574 WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-quoted");
3577 content
= webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (block
));
3578 text_length
= g_utf8_strlen (content
, -1);
3581 /* Wrap and quote the line */
3582 if (!remove_quoting
&& text_length
>= word_wrap_length
) {
3583 e_editor_dom_remove_quoting_from_element (block
);
3585 block
= e_editor_dom_wrap_paragraph_length (editor_page
, block
, length
);
3586 webkit_dom_node_normalize (WEBKIT_DOM_NODE (block
));
3587 e_editor_dom_quote_plain_text_element_after_wrapping (
3588 editor_page
, WEBKIT_DOM_ELEMENT (block
), citation_level
);
3589 selection_start_marker
= webkit_dom_document_get_element_by_id (
3590 document
, "-x-evo-selection-start-marker");
3591 if (!selection_start_marker
)
3592 dom_add_selection_markers_into_element_end (
3594 WEBKIT_DOM_ELEMENT (block
),
3598 e_editor_dom_selection_restore (editor_page
);
3599 do_spell_check
= TRUE
;
3603 e_editor_dom_selection_restore (editor_page
);
3607 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
3609 g_clear_object (&range
);
3613 body_input_event_cb (WebKitDOMElement
*element
,
3614 WebKitDOMEvent
*event
,
3615 EEditorPage
*editor_page
)
3617 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3619 /* Only process the input event if it was triggered by the key press
3620 * and not i.e. by execCommand. This behavior changed when the support
3621 * for beforeinput event was introduced in WebKit. */
3622 if (e_editor_page_is_processing_keypress_event (editor_page
))
3623 e_editor_dom_body_input_event_process (editor_page
, event
);
3625 e_editor_page_set_is_processing_keypress_event (editor_page
, FALSE
);
3629 e_editor_dom_remove_input_event_listener_from_body (EEditorPage
*editor_page
)
3631 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3633 if (!e_editor_page_get_body_input_event_removed (editor_page
)) {
3634 WebKitDOMDocument
*document
;
3636 document
= e_editor_page_get_document (editor_page
);
3638 webkit_dom_event_target_remove_event_listener (
3639 WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document
)),
3641 G_CALLBACK (body_input_event_cb
),
3644 e_editor_page_set_body_input_event_removed (editor_page
, TRUE
);
3649 e_editor_dom_register_input_event_listener_on_body (EEditorPage
*editor_page
)
3651 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3653 if (e_editor_page_get_body_input_event_removed (editor_page
)) {
3654 WebKitDOMDocument
*document
;
3656 document
= e_editor_page_get_document (editor_page
);
3658 webkit_dom_event_target_add_event_listener (
3659 WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document
)),
3661 G_CALLBACK (body_input_event_cb
),
3665 e_editor_page_set_body_input_event_removed (editor_page
, FALSE
);
3670 remove_empty_blocks (WebKitDOMDocument
*document
)
3673 WebKitDOMNodeList
*list
= NULL
;
3675 list
= webkit_dom_document_query_selector_all (
3676 document
, "blockquote[type=cite] > :empty:not(br)", NULL
);
3677 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;)
3678 remove_node (webkit_dom_node_list_item (list
, ii
));
3679 g_clear_object (&list
);
3681 list
= webkit_dom_document_query_selector_all (
3682 document
, "blockquote[type=cite]:empty", NULL
);
3683 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;)
3684 remove_node (webkit_dom_node_list_item (list
, ii
));
3685 g_clear_object (&list
);
3688 /* Following two functions are used when deleting the selection inside
3689 * the quoted content. The thing is that normally the quote marks are not
3690 * selectable by user. But this caused a lot of problems for WebKit when removing
3691 * the selection. This will avoid it as when the delete or backspace key is pressed
3692 * we will make the quote marks user selectable so they will act as any other text.
3693 * On HTML keyup event callback we will make them again non-selectable. */
3695 e_editor_dom_disable_quote_marks_select (EEditorPage
*editor_page
)
3697 WebKitDOMDocument
*document
;
3698 WebKitDOMHTMLHeadElement
*head
;
3699 WebKitDOMElement
*style_element
;
3701 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3703 document
= e_editor_page_get_document (editor_page
);
3704 head
= webkit_dom_document_get_head (document
);
3706 if (!webkit_dom_document_get_element_by_id (document
, "-x-evo-quote-style")) {
3707 style_element
= webkit_dom_document_create_element (document
, "style", NULL
);
3708 webkit_dom_element_set_id (style_element
, "-x-evo-quote-style");
3709 webkit_dom_element_set_attribute (style_element
, "type", "text/css", NULL
);
3710 webkit_dom_element_set_inner_html (
3712 ".-x-evo-quoted { -webkit-user-select: none; }",
3714 webkit_dom_node_append_child (
3715 WEBKIT_DOM_NODE (head
), WEBKIT_DOM_NODE (style_element
), NULL
);
3720 enable_quote_marks_select (WebKitDOMDocument
*document
)
3722 WebKitDOMElement
*style_element
;
3724 if ((style_element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-quote-style")))
3725 remove_node (WEBKIT_DOM_NODE (style_element
));
3729 e_editor_dom_remove_node_and_parents_if_empty (WebKitDOMNode
*node
)
3731 WebKitDOMNode
*parent
;
3733 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (node
));
3735 remove_node (WEBKIT_DOM_NODE (node
));
3737 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
3740 tmp
= webkit_dom_node_get_parent_node (parent
);
3741 remove_node_if_empty (parent
);
3747 e_editor_dom_merge_siblings_if_necessary (EEditorPage
*editor_page
,
3748 WebKitDOMDocumentFragment
*deleted_content
)
3750 WebKitDOMDocument
*document
;
3751 WebKitDOMElement
*element
, *prev_element
;
3752 WebKitDOMNode
*child
;
3753 WebKitDOMNodeList
*list
= NULL
;
3754 gboolean equal_nodes
;
3757 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3759 document
= e_editor_page_get_document (editor_page
);
3761 if ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-main-cite")))
3762 webkit_dom_element_remove_attribute (element
, "id");
3764 element
= webkit_dom_document_query_selector (document
, "blockquote:not([data-evo-query-skip]) + blockquote:not([data-evo-query-skip])", NULL
);
3768 child
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
3769 if (WEBKIT_DOM_IS_ELEMENT (child
))
3770 prev_element
= WEBKIT_DOM_ELEMENT (child
);
3774 equal_nodes
= webkit_dom_node_is_equal_node (
3775 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element
), FALSE
, NULL
),
3776 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (prev_element
), FALSE
, NULL
));
3779 if (webkit_dom_element_get_child_element_count (element
) >
3780 webkit_dom_element_get_child_element_count (prev_element
)) {
3781 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
))))
3782 webkit_dom_node_append_child (
3783 WEBKIT_DOM_NODE (prev_element
), child
, NULL
);
3784 remove_node (WEBKIT_DOM_NODE (element
));
3786 while ((child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (prev_element
))))
3787 webkit_dom_node_insert_before (
3788 WEBKIT_DOM_NODE (element
),
3790 webkit_dom_node_get_first_child (
3791 WEBKIT_DOM_NODE (element
)),
3793 remove_node (WEBKIT_DOM_NODE (prev_element
));
3796 webkit_dom_element_set_attribute (element
, "data-evo-query-skip", "", NULL
);
3798 element
= webkit_dom_document_query_selector (document
, "blockquote:not([data-evo-query-skip]) + blockquote:not([data-evo-query-skip])", NULL
);
3803 list
= webkit_dom_document_query_selector_all (
3804 document
, "blockquote[data-evo-query-skip]", NULL
);
3805 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
3806 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
3807 webkit_dom_element_remove_attribute (
3808 WEBKIT_DOM_ELEMENT (node
), "data-evo-query-skip");
3810 g_clear_object (&list
);
3812 if (!deleted_content
)
3815 /* Replace the corrupted signatures with the right one. */
3816 element
= webkit_dom_document_query_selector (
3817 document
, ".-x-evo-signature-wrapper + .-x-evo-signature-wrapper", NULL
);
3819 WebKitDOMElement
*right_signature
;
3821 right_signature
= webkit_dom_document_fragment_query_selector (
3822 deleted_content
, ".-x-evo-signature-wrapper", NULL
);
3823 remove_node (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
)));
3824 webkit_dom_node_replace_child (
3825 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
3826 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (right_signature
), TRUE
, NULL
),
3827 WEBKIT_DOM_NODE (element
),
3832 /* This will fix the structure after the situations where some text
3833 * inside the quoted content is selected and afterwards deleted with
3834 * BackSpace or Delete. */
3836 e_editor_dom_body_key_up_event_process_backspace_or_delete (EEditorPage
*editor_page
,
3839 WebKitDOMDocument
*document
;
3840 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
3841 WebKitDOMNode
*parent
, *node
;
3844 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3846 if (e_editor_page_get_html_mode (editor_page
)) {
3848 e_editor_dom_selection_save (editor_page
);
3849 e_editor_dom_merge_siblings_if_necessary (editor_page
, NULL
);
3850 e_editor_dom_selection_restore (editor_page
);
3855 document
= e_editor_page_get_document (editor_page
);
3856 e_editor_dom_disable_quote_marks_select (editor_page
);
3857 /* Remove empty blocks if presented. */
3858 remove_empty_blocks (document
);
3860 e_editor_dom_selection_save (editor_page
);
3861 selection_start_marker
= webkit_dom_document_get_element_by_id (
3862 document
, "-x-evo-selection-start-marker");
3863 selection_end_marker
= webkit_dom_document_get_element_by_id (
3864 document
, "-x-evo-selection-end-marker");
3866 /* If we deleted a selection the caret will be inside the quote marks, fix it. */
3867 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
3868 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-quote-character")) {
3869 parent
= webkit_dom_node_get_parent_node (parent
);
3870 webkit_dom_node_insert_before (
3871 webkit_dom_node_get_parent_node (parent
),
3872 WEBKIT_DOM_NODE (selection_end_marker
),
3873 webkit_dom_node_get_next_sibling (parent
),
3875 webkit_dom_node_insert_before (
3876 webkit_dom_node_get_parent_node (parent
),
3877 WEBKIT_DOM_NODE (selection_start_marker
),
3878 webkit_dom_node_get_next_sibling (parent
),
3882 /* Under some circumstances we will end with block inside the citation
3883 * that has the quote marks removed and we have to reinsert them back. */
3884 level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_start_marker
));
3885 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
));
3886 if (level
> 0 && node
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
3887 WebKitDOMElement
*block
;
3889 block
= WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (
3890 WEBKIT_DOM_NODE (selection_start_marker
)));
3892 e_editor_dom_remove_quoting_from_element (block
);
3893 if (webkit_dom_element_has_attribute (block
, "data-evo-paragraph")) {
3894 gint length
, word_wrap_length
;
3896 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
3897 length
= word_wrap_length
- 2 * level
;
3898 block
= e_editor_dom_wrap_paragraph_length (editor_page
, block
, length
);
3899 webkit_dom_node_normalize (WEBKIT_DOM_NODE (block
));
3901 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, block
, level
);
3902 } else if (level
> 0 && !node
) {
3903 WebKitDOMNode
*prev_sibling
;
3905 prev_sibling
= webkit_dom_node_get_previous_sibling (
3906 WEBKIT_DOM_NODE (selection_start_marker
));
3907 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
) &&
3908 element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-quoted") &&
3909 !webkit_dom_node_get_previous_sibling (prev_sibling
)) {
3910 webkit_dom_node_append_child (
3911 webkit_dom_node_get_parent_node (parent
),
3912 WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)),
3917 e_editor_dom_merge_siblings_if_necessary (editor_page
, NULL
);
3919 e_editor_dom_selection_restore (editor_page
);
3920 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
3924 e_editor_dom_body_key_up_event_process_return_key (EEditorPage
*editor_page
)
3926 WebKitDOMDocument
*document
;
3927 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
3928 WebKitDOMNode
*parent
;
3930 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3932 /* If the return is pressed in an unordered list in plain text mode
3933 * the caret is moved to the "*" character before the newly inserted
3934 * item. It looks like it is not enough that the item has BR element
3935 * inside, but we have to again use the zero width space character
3936 * to fix the situation. */
3937 if (e_editor_page_get_html_mode (editor_page
))
3940 document
= e_editor_page_get_document (editor_page
);
3941 e_editor_dom_selection_save (editor_page
);
3943 selection_start_marker
= webkit_dom_document_get_element_by_id (
3944 document
, "-x-evo-selection-start-marker");
3945 selection_end_marker
= webkit_dom_document_get_element_by_id (
3946 document
, "-x-evo-selection-end-marker");
3948 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
3949 if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent
) ||
3950 !WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (webkit_dom_node_get_parent_node (parent
))) {
3951 e_editor_dom_selection_restore (editor_page
);
3955 if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
)) &&
3956 (!webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
)) ||
3957 WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
)))))
3958 webkit_dom_element_insert_adjacent_text (
3959 WEBKIT_DOM_ELEMENT (parent
),
3961 UNICODE_ZERO_WIDTH_SPACE
,
3964 e_editor_dom_selection_restore (editor_page
);
3968 body_keyup_event_cb (WebKitDOMElement
*element
,
3969 WebKitDOMUIEvent
*event
,
3970 EEditorPage
*editor_page
)
3972 WebKitDOMDocument
*document
;
3975 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
3977 document
= webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element
));
3978 if (!e_editor_page_is_composition_in_progress (editor_page
))
3979 e_editor_dom_register_input_event_listener_on_body (editor_page
);
3981 if (!e_editor_dom_selection_is_collapsed (editor_page
))
3984 key_code
= webkit_dom_ui_event_get_key_code (event
);
3985 if (key_code
== HTML_KEY_CODE_BACKSPACE
|| key_code
== HTML_KEY_CODE_DELETE
) {
3986 e_editor_dom_body_key_up_event_process_backspace_or_delete (editor_page
, key_code
== HTML_KEY_CODE_DELETE
);
3988 /* The content was wrapped and the coordinates
3989 * of caret could be changed, so renew them. But
3990 * only do that when we are not redoing a history
3991 * event, otherwise it would modify the history. */
3992 if (e_editor_page_get_renew_history_after_coordinates (editor_page
)) {
3993 EEditorHistoryEvent
*ev
= NULL
;
3994 EEditorUndoRedoManager
*manager
;
3996 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
3997 ev
= e_editor_undo_redo_manager_get_current_history_event (manager
);
3998 e_editor_dom_selection_get_coordinates (editor_page
,
4005 e_editor_page_emit_content_changed (editor_page
);
4006 } else if (key_code
== HTML_KEY_CODE_CONTROL
)
4007 dom_set_links_active (document
, FALSE
);
4008 else if (key_code
== HTML_KEY_CODE_RETURN
)
4009 e_editor_dom_body_key_up_event_process_return_key (editor_page
);
4013 fix_structure_after_pasting_multiline_content (WebKitDOMNode
*node
)
4015 WebKitDOMNode
*first_child
, *parent
;
4017 /* When pasting content that does not contain just the
4018 * one line text WebKit inserts all the content after the
4019 * first line into one element. So we have to take it out
4020 * of this element and insert it after that element. */
4021 parent
= webkit_dom_node_get_parent_node (node
);
4022 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
))
4024 first_child
= webkit_dom_node_get_first_child (parent
);
4025 while (first_child
) {
4026 WebKitDOMNode
*next_child
=
4027 webkit_dom_node_get_next_sibling (first_child
);
4028 if (webkit_dom_node_has_child_nodes (first_child
))
4029 webkit_dom_node_insert_before (
4030 webkit_dom_node_get_parent_node (parent
),
4034 first_child
= next_child
;
4039 delete_hidden_space (EEditorPage
*editor_page
)
4041 WebKitDOMDocument
*document
;
4042 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *block
;
4043 gint citation_level
;
4045 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
4047 document
= e_editor_page_get_document (editor_page
);
4049 selection_start_marker
= webkit_dom_document_get_element_by_id (
4050 document
, "-x-evo-selection-start-marker");
4051 selection_end_marker
= webkit_dom_document_get_element_by_id (
4052 document
, "-x-evo-selection-end-marker");
4054 if (!selection_start_marker
|| !selection_end_marker
)
4057 block
= WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (
4058 WEBKIT_DOM_NODE (selection_start_marker
)));
4060 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_start_marker
));
4062 if (selection_start_marker
&& citation_level
> 0) {
4063 EEditorUndoRedoManager
*manager
;
4064 EEditorHistoryEvent
*ev
= NULL
;
4065 WebKitDOMNode
*node
;
4066 WebKitDOMDocumentFragment
*fragment
;
4068 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
4070 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
4071 if (!(WEBKIT_DOM_IS_ELEMENT (node
) &&
4072 element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-quoted")))
4075 node
= webkit_dom_node_get_previous_sibling (node
);
4076 if (!(WEBKIT_DOM_IS_ELEMENT (node
) &&
4077 element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-wrap-br")))
4080 node
= webkit_dom_node_get_previous_sibling (node
);
4081 if (!(WEBKIT_DOM_IS_ELEMENT (node
) &&
4082 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node
), "data-hidden-space")))
4085 ev
= g_new0 (EEditorHistoryEvent
, 1);
4086 ev
->type
= HISTORY_DELETE
;
4088 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
4092 e_editor_dom_wrap_and_quote_element (editor_page
, block
);
4094 fragment
= webkit_dom_document_create_document_fragment (document
);
4095 webkit_dom_node_append_child (
4096 WEBKIT_DOM_NODE (fragment
),
4098 webkit_dom_document_create_text_node (document
, " ")),
4100 ev
->data
.fragment
= g_object_ref (fragment
);
4102 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
4104 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
4113 caret_is_on_the_line_beginning_html (WebKitDOMDocument
*document
)
4115 gboolean ret_val
= FALSE
;
4116 WebKitDOMDOMWindow
*dom_window
= NULL
;
4117 WebKitDOMDOMSelection
*dom_selection
= NULL
;
4118 WebKitDOMRange
*tmp_range
= NULL
, *actual_range
= NULL
;
4120 dom_window
= webkit_dom_document_get_default_view (document
);
4121 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
4123 actual_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
4125 webkit_dom_dom_selection_modify (dom_selection
, "move", "left", "lineBoundary");
4127 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
4129 if (webkit_dom_range_compare_boundary_points (tmp_range
, WEBKIT_DOM_RANGE_START_TO_START
, actual_range
, NULL
) == 0)
4132 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
4133 webkit_dom_dom_selection_add_range (dom_selection
, actual_range
);
4135 g_clear_object (&tmp_range
);
4136 g_clear_object (&actual_range
);
4138 g_clear_object (&dom_window
);
4139 g_clear_object (&dom_selection
);
4145 is_empty_quoted_element (WebKitDOMElement
*element
)
4147 WebKitDOMNode
*node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
));
4149 if (!WEBKIT_DOM_IS_ELEMENT (node
) || !element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-quoted"))
4152 if (!(node
= webkit_dom_node_get_next_sibling (node
)))
4155 if (WEBKIT_DOM_IS_TEXT (node
)) {
4158 content
= webkit_dom_node_get_text_content (node
);
4159 if (content
&& *content
) {
4165 return webkit_dom_node_get_next_sibling (node
) ? FALSE
: TRUE
;
4168 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
))
4169 return webkit_dom_node_get_next_sibling (node
) ? FALSE
: TRUE
;
4171 if (!WEBKIT_DOM_IS_ELEMENT (node
) || !element_has_id (WEBKIT_DOM_ELEMENT (node
), "-x-evo-selection-start-marker"))
4174 if (!(node
= webkit_dom_node_get_next_sibling (node
)))
4177 if (!WEBKIT_DOM_IS_ELEMENT (node
) || !element_has_id (WEBKIT_DOM_ELEMENT (node
), "-x-evo-selection-end-marker"))
4180 if (!(node
= webkit_dom_node_get_next_sibling (node
)))
4183 if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
4184 if (WEBKIT_DOM_IS_TEXT (node
)) {
4187 content
= webkit_dom_node_get_text_content (node
);
4188 if (content
&& *content
) {
4194 return webkit_dom_node_get_next_sibling (node
) ? FALSE
: TRUE
;
4199 if (!(node
= webkit_dom_node_get_next_sibling (node
)))
4206 e_editor_dom_move_quoted_block_level_up (EEditorPage
*editor_page
)
4208 WebKitDOMDocument
*document
;
4209 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
4210 WebKitDOMNode
*block
;
4211 EEditorHistoryEvent
*ev
= NULL
;
4212 EEditorUndoRedoManager
*manager
;
4214 gint citation_level
, success
= FALSE
;
4216 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
4218 document
= e_editor_page_get_document (editor_page
);
4219 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
4220 html_mode
= e_editor_page_get_html_mode (editor_page
);
4222 selection_start_marker
= webkit_dom_document_get_element_by_id (
4223 document
, "-x-evo-selection-start-marker");
4224 selection_end_marker
= webkit_dom_document_get_element_by_id (
4225 document
, "-x-evo-selection-end-marker");
4227 if (!selection_start_marker
|| !selection_end_marker
)
4230 block
= e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_start_marker
));
4232 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_start_marker
));
4234 if (selection_start_marker
&& citation_level
> 0) {
4235 if (webkit_dom_element_query_selector (
4236 WEBKIT_DOM_ELEMENT (block
), ".-x-evo-quoted", NULL
)) {
4238 WebKitDOMNode
*prev_sibling
;
4240 webkit_dom_node_normalize (block
);
4242 prev_sibling
= webkit_dom_node_get_previous_sibling (
4243 WEBKIT_DOM_NODE (selection_start_marker
));
4245 if (!prev_sibling
) {
4246 WebKitDOMNode
*parent
;
4248 parent
= webkit_dom_node_get_parent_node (
4249 WEBKIT_DOM_NODE (selection_start_marker
));
4250 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
))
4251 prev_sibling
= webkit_dom_node_get_previous_sibling (parent
);
4254 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
))
4255 success
= element_has_class (
4256 WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-quoted");
4258 /* We really have to be in the beginning of paragraph and
4259 * not on the beginning of some line in the paragraph */
4260 if (success
&& webkit_dom_node_get_previous_sibling (prev_sibling
))
4265 webkit_dom_node_normalize (block
);
4267 success
= caret_is_on_the_line_beginning_html (document
);
4268 if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
)))
4269 block
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
4276 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
4277 ev
= g_new0 (EEditorHistoryEvent
, 1);
4278 ev
->type
= HISTORY_UNQUOTE
;
4280 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
4281 ev
->data
.dom
.from
= g_object_ref (webkit_dom_node_clone_node_with_error (block
, TRUE
, NULL
));
4284 if (citation_level
== 1) {
4285 gboolean is_empty_quoted_block
= FALSE
;
4286 gchar
*inner_html
= NULL
;
4287 WebKitDOMElement
*paragraph
, *element
;
4289 if (WEBKIT_DOM_IS_ELEMENT (block
)) {
4290 is_empty_quoted_block
= is_empty_quoted_element (WEBKIT_DOM_ELEMENT (block
));
4291 inner_html
= webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (block
));
4292 webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (block
), "-x-evo-to-remove");
4295 paragraph
= e_editor_dom_insert_new_line_into_citation (editor_page
, inner_html
);
4296 g_free (inner_html
);
4299 if (!(webkit_dom_element_query_selector (paragraph
, "#-x-evo-selection-start-marker", NULL
)))
4300 webkit_dom_node_insert_before (
4301 WEBKIT_DOM_NODE (paragraph
),
4302 WEBKIT_DOM_NODE (selection_start_marker
),
4303 webkit_dom_node_get_first_child (
4304 WEBKIT_DOM_NODE (paragraph
)),
4307 if (!(webkit_dom_element_query_selector (paragraph
, "#-x-evo-selection-end-marker", NULL
)))
4308 webkit_dom_node_insert_before (
4309 WEBKIT_DOM_NODE (paragraph
),
4310 WEBKIT_DOM_NODE (selection_end_marker
),
4311 webkit_dom_node_get_first_child (
4312 WEBKIT_DOM_NODE (paragraph
)),
4315 e_editor_dom_remove_quoting_from_element (paragraph
);
4316 e_editor_dom_remove_wrapping_from_element (paragraph
);
4318 /* Moving PRE block from citation to body */
4319 if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block
) && !is_empty_quoted_block
) {
4320 WebKitDOMElement
*pre
;
4321 WebKitDOMNode
*child
;
4323 pre
= webkit_dom_document_create_element (document
, "pre", NULL
);
4324 webkit_dom_node_insert_before (
4325 webkit_dom_node_get_parent_node (
4326 WEBKIT_DOM_NODE (paragraph
)),
4327 WEBKIT_DOM_NODE (pre
),
4328 WEBKIT_DOM_NODE (paragraph
),
4331 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph
))))
4332 webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre
), child
, NULL
);
4334 remove_node (WEBKIT_DOM_NODE (paragraph
));
4340 remove_node (block
);
4342 while ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-to-remove")))
4343 remove_node (WEBKIT_DOM_NODE (element
));
4346 remove_node_if_empty (
4347 webkit_dom_node_get_next_sibling (
4348 WEBKIT_DOM_NODE (paragraph
)));
4351 if (citation_level
> 1) {
4352 WebKitDOMNode
*parent
;
4355 webkit_dom_node_insert_before (
4357 WEBKIT_DOM_NODE (selection_start_marker
),
4358 webkit_dom_node_get_first_child (block
),
4360 webkit_dom_node_insert_before (
4362 WEBKIT_DOM_NODE (selection_end_marker
),
4363 webkit_dom_node_get_first_child (block
),
4368 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
4369 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
4371 parent
= webkit_dom_node_get_parent_node (block
);
4373 if (!webkit_dom_node_get_previous_sibling (block
)) {
4374 /* Currect block is in the beginning of citation, just move it
4375 * before the citation where already is */
4376 webkit_dom_node_insert_before (
4377 webkit_dom_node_get_parent_node (parent
),
4381 } else if (!webkit_dom_node_get_next_sibling (block
)) {
4382 /* Currect block is at the end of the citation, just move it
4383 * after the citation where already is */
4384 webkit_dom_node_insert_before (
4385 webkit_dom_node_get_parent_node (parent
),
4387 webkit_dom_node_get_next_sibling (parent
),
4390 /* Current block is somewhere in the middle of the citation
4391 * so we need to split the citation and insert the block into
4392 * the citation that is one level lower */
4393 WebKitDOMNode
*clone
, *child
;
4395 clone
= webkit_dom_node_clone_node_with_error (parent
, FALSE
, NULL
);
4397 /* Move nodes that are after the currect block into the
4399 child
= webkit_dom_node_get_next_sibling (block
);
4401 WebKitDOMNode
*next
= webkit_dom_node_get_next_sibling (child
);
4402 webkit_dom_node_append_child (clone
, child
, NULL
);
4406 clone
= webkit_dom_node_insert_before (
4407 webkit_dom_node_get_parent_node (parent
),
4409 webkit_dom_node_get_next_sibling (parent
),
4412 webkit_dom_node_insert_before (
4413 webkit_dom_node_get_parent_node (parent
),
4419 e_editor_dom_wrap_and_quote_element (editor_page
, WEBKIT_DOM_ELEMENT (block
));
4422 remove_empty_blocks (document
);
4425 e_editor_dom_selection_get_coordinates (editor_page
,
4430 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
4437 prevent_from_deleting_last_element_in_body (WebKitDOMDocument
*document
)
4439 gboolean ret_val
= FALSE
;
4440 WebKitDOMHTMLElement
*body
;
4441 WebKitDOMNode
*node
;
4443 body
= webkit_dom_document_get_body (document
);
4445 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
4446 if (!node
|| !webkit_dom_node_get_next_sibling (node
)) {
4449 content
= webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body
));
4451 if (!content
|| !*content
)
4456 if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body
), "img", NULL
))
4464 insert_quote_symbols (WebKitDOMDocument
*document
,
4465 WebKitDOMHTMLElement
*element
,
4469 WebKitDOMElement
*quote_element
;
4471 if (!WEBKIT_DOM_IS_ELEMENT (element
))
4474 quotation
= get_quotation_for_level (quote_level
);
4476 quote_element
= webkit_dom_document_create_element (document
, "span", NULL
);
4477 element_add_class (quote_element
, "-x-evo-quoted");
4479 webkit_dom_element_set_inner_html (quote_element
, quotation
, NULL
);
4480 webkit_dom_node_insert_before (
4481 WEBKIT_DOM_NODE (element
),
4482 WEBKIT_DOM_NODE (quote_element
),
4483 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
)),
4490 quote_node (WebKitDOMDocument
*document
,
4491 WebKitDOMNode
*node
,
4494 WebKitDOMNode
*parent
, *next_sibling
;
4496 /* Don't quote when we are not in citation */
4497 if (quote_level
== 0)
4500 if (WEBKIT_DOM_IS_COMMENT (node
))
4503 if (WEBKIT_DOM_IS_ELEMENT (node
)) {
4504 insert_quote_symbols (document
, WEBKIT_DOM_HTML_ELEMENT (node
), quote_level
);
4508 next_sibling
= webkit_dom_node_get_next_sibling (node
);
4510 /* Skip the BR between first blockquote and pre */
4511 if (quote_level
== 1 && next_sibling
&& WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling
))
4514 parent
= webkit_dom_node_get_parent_node (node
);
4516 insert_quote_symbols (
4517 document
, WEBKIT_DOM_HTML_ELEMENT (parent
), quote_level
);
4521 insert_quote_symbols_before_node (WebKitDOMDocument
*document
,
4522 WebKitDOMNode
*node
,
4524 gboolean is_html_node
)
4526 gboolean skip
, wrap_br
;
4528 WebKitDOMElement
*element
;
4530 quotation
= get_quotation_for_level (quote_level
);
4531 element
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
4532 element_add_class (element
, "-x-evo-quoted");
4533 webkit_dom_element_set_inner_html (element
, quotation
, NULL
);
4535 /* Don't insert temporary BR before BR that is used for wrapping */
4536 skip
= WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
);
4537 wrap_br
= element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-wrap-br");
4538 skip
= skip
&& wrap_br
;
4540 if (is_html_node
&& !skip
) {
4541 WebKitDOMElement
*new_br
;
4543 new_br
= webkit_dom_document_create_element (document
, "br", NULL
);
4544 element_add_class (new_br
, "-x-evo-temp-br");
4546 webkit_dom_node_insert_before (
4547 webkit_dom_node_get_parent_node (node
),
4548 WEBKIT_DOM_NODE (new_br
),
4553 webkit_dom_node_insert_before (
4554 webkit_dom_node_get_parent_node (node
),
4555 WEBKIT_DOM_NODE (element
),
4559 if (is_html_node
&& !wrap_br
)
4566 check_if_suppress_next_node (WebKitDOMNode
*node
)
4571 if (node
&& WEBKIT_DOM_IS_ELEMENT (node
))
4572 if (e_editor_dom_is_selection_position_node (node
))
4573 if (!webkit_dom_node_get_previous_sibling (node
))
4580 quote_br_node (WebKitDOMNode
*node
,
4583 gchar
*quotation
, *content
;
4585 quotation
= get_quotation_for_level (quote_level
);
4587 content
= g_strconcat (
4588 "<span class=\"-x-evo-quoted\">",
4590 "</span><br class=\"-x-evo-temp-br\">",
4593 webkit_dom_element_set_outer_html (
4594 WEBKIT_DOM_ELEMENT (node
), content
, NULL
);
4601 quote_plain_text_recursive (WebKitDOMDocument
*document
,
4602 WebKitDOMNode
*block
,
4603 WebKitDOMNode
*start_node
,
4606 gboolean skip_node
= FALSE
;
4607 gboolean move_next
= FALSE
;
4608 gboolean suppress_next
= FALSE
;
4609 gboolean is_html_node
= FALSE
;
4610 gboolean next
= FALSE
;
4611 WebKitDOMNode
*node
, *next_sibling
, *prev_sibling
;
4613 node
= webkit_dom_node_get_first_child (block
);
4618 is_html_node
= FALSE
;
4620 if (WEBKIT_DOM_IS_COMMENT (node
) ||
4621 WEBKIT_DOM_IS_HTML_META_ELEMENT (node
) ||
4622 WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node
) ||
4623 WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node
)) {
4629 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
4630 next_sibling
= webkit_dom_node_get_next_sibling (node
);
4632 if (WEBKIT_DOM_IS_TEXT (node
)) {
4633 /* Start quoting after we are in blockquote */
4634 if (quote_level
> 0 && !suppress_next
) {
4635 /* When quoting text node, we are wrappering it and
4636 * afterwards replacing it with that wrapper, thus asking
4637 * for next_sibling after quoting will return NULL bacause
4638 * that node don't exist anymore */
4639 quote_node (document
, node
, quote_level
);
4640 node
= next_sibling
;
4647 if (!(WEBKIT_DOM_IS_ELEMENT (node
) || WEBKIT_DOM_IS_HTML_ELEMENT (node
)))
4650 if (e_editor_dom_is_selection_position_node (node
)) {
4651 /* If there is collapsed selection in the beginning of line
4652 * we cannot suppress first text that is after the end of
4654 suppress_next
= check_if_suppress_next_node (prev_sibling
);
4661 if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
) &&
4662 webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node
)) != 0)
4665 /* Even in plain text mode we can have some basic html element
4666 * like anchor and others. When Forwaring e-mail as Quoted EMFormat
4667 * generates header that contains <b> tags (bold font).
4668 * We have to treat these elements separately to avoid
4669 * modifications of theirs inner texts */
4671 WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
) ||
4672 element_has_tag (WEBKIT_DOM_ELEMENT (node
), "b") ||
4673 element_has_tag (WEBKIT_DOM_ELEMENT (node
), "i") ||
4674 element_has_tag (WEBKIT_DOM_ELEMENT (node
), "u") ||
4675 element_has_class (WEBKIT_DOM_ELEMENT (node
), "Apple-tab-span");
4682 WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
) &&
4684 WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-wrap-br");
4686 if (!prev_sibling
|| wrap_br
) {
4687 insert_quote_symbols_before_node (
4688 document
, node
, quote_level
, FALSE
);
4689 if (!prev_sibling
&& next_sibling
&& WEBKIT_DOM_IS_TEXT (next_sibling
))
4690 suppress_next
= TRUE
;
4693 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
) && !wrap_br
)
4694 insert_quote_symbols_before_node (
4695 document
, prev_sibling
, quote_level
, TRUE
);
4701 /* If element doesn't have children, we can quote it */
4702 if (e_editor_dom_node_is_citation_node (node
)) {
4703 /* Citation with just text inside */
4704 quote_node (document
, node
, quote_level
+ 1);
4710 if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
4711 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling
)) {
4716 } else if (element_has_id (WEBKIT_DOM_ELEMENT (node
), "-x-evo-first-br") ||
4717 element_has_id (WEBKIT_DOM_ELEMENT (node
), "-x-evo-last-br")) {
4718 quote_br_node (node
, quote_level
);
4719 node
= next_sibling
;
4724 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
)) {
4725 quote_br_node (prev_sibling
, quote_level
);
4726 node
= next_sibling
;
4731 if (!prev_sibling
&& !next_sibling
) {
4732 WebKitDOMNode
*parent
= webkit_dom_node_get_parent_node (node
);
4734 if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent
) ||
4735 WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent
) ||
4736 (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent
) &&
4737 !e_editor_dom_node_is_citation_node (parent
))) {
4738 insert_quote_symbols_before_node (
4739 document
, node
, quote_level
, FALSE
);
4745 if (e_editor_dom_node_is_citation_node (prev_sibling
)) {
4746 insert_quote_symbols_before_node (
4747 document
, node
, quote_level
, FALSE
);
4751 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
) &&
4752 !next_sibling
&& WEBKIT_DOM_IS_ELEMENT (prev_sibling
) &&
4753 e_editor_dom_is_selection_position_node (prev_sibling
)) {
4754 insert_quote_symbols_before_node (
4755 document
, node
, quote_level
, FALSE
);
4759 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
4760 if (!prev_sibling
&& !next_sibling
) {
4761 insert_quote_symbols_before_node (
4762 document
, node
, quote_level
, FALSE
);
4769 quote_node (document
, node
, quote_level
);
4775 if (e_editor_dom_node_is_citation_node (node
)) {
4776 /* Go deeper and increase level */
4777 quote_plain_text_recursive (
4778 document
, node
, start_node
, quote_level
+ 1);
4781 quote_plain_text_recursive (
4782 document
, node
, start_node
, quote_level
);
4787 suppress_next
= FALSE
;
4795 /* Move to next node */
4796 if (!move_next
&& webkit_dom_node_has_child_nodes (node
)) {
4797 node
= webkit_dom_node_get_first_child (node
);
4798 } else if (webkit_dom_node_get_next_sibling (node
)) {
4799 node
= webkit_dom_node_get_next_sibling (node
);
4808 e_editor_dom_quote_plain_text_element (EEditorPage
*editor_page
,
4809 WebKitDOMElement
*element
)
4811 WebKitDOMDocument
*document
;
4812 WebKitDOMNode
*element_clone
;
4813 WebKitDOMHTMLCollection
*collection
= NULL
;
4816 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
4818 document
= e_editor_page_get_document (editor_page
);
4819 element_clone
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element
), TRUE
, NULL
);
4820 level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
4822 /* Remove old quote characters if the exists */
4823 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
4824 WEBKIT_DOM_ELEMENT (element_clone
), "-x-evo-quoted");
4825 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;)
4826 remove_node (webkit_dom_html_collection_item (collection
, ii
));
4827 g_clear_object (&collection
);
4829 webkit_dom_node_normalize (element_clone
);
4830 quote_plain_text_recursive (
4831 document
, element_clone
, element_clone
, level
);
4833 /* Replace old element with one, that is quoted */
4834 webkit_dom_node_replace_child (
4835 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
4837 WEBKIT_DOM_NODE (element
),
4840 return WEBKIT_DOM_ELEMENT (element_clone
);
4844 * dom_quote_plain_text:
4846 * Quote text inside citation blockquotes in plain text mode.
4848 * As this function is cloning and replacing all citation blockquotes keep on
4849 * mind that any pointers to nodes inside these blockquotes will be invalidated.
4851 static WebKitDOMElement
*
4852 dom_quote_plain_text (WebKitDOMDocument
*document
)
4854 WebKitDOMHTMLElement
*body
;
4855 WebKitDOMNode
*body_clone
;
4856 WebKitDOMNamedNodeMap
*attributes
= NULL
;
4857 WebKitDOMNodeList
*list
= NULL
;
4858 WebKitDOMElement
*element
;
4860 gulong attributes_length
;
4862 /* Check if the document is already quoted */
4863 element
= webkit_dom_document_query_selector (
4864 document
, ".-x-evo-quoted", NULL
);
4868 body
= webkit_dom_document_get_body (document
);
4869 body_clone
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body
), TRUE
, NULL
);
4871 /* Clean unwanted spaces before and after blockquotes */
4872 list
= webkit_dom_element_query_selector_all (
4873 WEBKIT_DOM_ELEMENT (body_clone
), "blockquote[type|=cite]", NULL
);
4874 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
4875 WebKitDOMNode
*blockquote
= webkit_dom_node_list_item (list
, ii
);
4876 WebKitDOMNode
*prev_sibling
= webkit_dom_node_get_previous_sibling (blockquote
);
4877 WebKitDOMNode
*next_sibling
= webkit_dom_node_get_next_sibling (blockquote
);
4879 if (prev_sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
))
4880 remove_node (prev_sibling
);
4882 if (next_sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
))
4883 remove_node (next_sibling
);
4885 if (webkit_dom_node_has_child_nodes (blockquote
)) {
4886 WebKitDOMNode
*child
= webkit_dom_node_get_first_child (blockquote
);
4887 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
4888 remove_node (child
);
4891 g_clear_object (&list
);
4893 webkit_dom_node_normalize (body_clone
);
4894 quote_plain_text_recursive (document
, body_clone
, body_clone
, 0);
4896 /* Copy attributes */
4897 attributes
= webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body
));
4898 attributes_length
= webkit_dom_named_node_map_get_length (attributes
);
4899 for (ii
= 0; ii
< attributes_length
; ii
++) {
4900 gchar
*name
, *value
;
4901 WebKitDOMNode
*node
= webkit_dom_named_node_map_item (attributes
, ii
);
4903 name
= webkit_dom_attr_get_name (WEBKIT_DOM_ATTR (node
));
4904 value
= webkit_dom_node_get_node_value (node
);
4906 webkit_dom_element_set_attribute (
4907 WEBKIT_DOM_ELEMENT (body_clone
), name
, value
, NULL
);
4912 g_clear_object (&attributes
);
4914 /* Replace old BODY with one, that is quoted */
4915 webkit_dom_node_replace_child (
4916 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body
)),
4918 WEBKIT_DOM_NODE (body
),
4921 return WEBKIT_DOM_ELEMENT (body_clone
);
4925 * dom_dequote_plain_text:
4927 * Dequote already quoted plain text in editor.
4928 * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise
4932 dom_dequote_plain_text (WebKitDOMDocument
*document
)
4934 WebKitDOMNodeList
*paragraphs
= NULL
;
4937 paragraphs
= webkit_dom_document_query_selector_all (
4938 document
, "blockquote[type=cite]", NULL
);
4939 for (ii
= webkit_dom_node_list_get_length (paragraphs
); ii
--;) {
4940 WebKitDOMElement
*element
;
4942 element
= WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs
, ii
));
4944 if (e_editor_dom_node_is_citation_node (WEBKIT_DOM_NODE (element
)))
4945 e_editor_dom_remove_quoting_from_element (element
);
4947 g_clear_object (¶graphs
);
4951 create_anchor_for_link (const GMatchInfo
*info
,
4955 gboolean link_surrounded
, ending_with_nbsp
= FALSE
;
4956 gint offset
= 0, truncate_from_end
= 0;
4957 gint match_start
, match_end
;
4959 const gchar
*end_of_match
= NULL
;
4960 const gchar
*nbsp_match
= NULL
;
4962 match
= g_match_info_fetch (info
, 0);
4963 g_match_info_fetch_pos (info
, 0, &match_start
, &match_end
);
4965 if (g_str_has_suffix (match
, " ")) {
4966 ending_with_nbsp
= TRUE
;
4967 truncate_from_end
= 6;
4970 if (g_str_has_prefix (match
, " "))
4973 end_of_match
= match
+ match_end
- match_start
- 1;
4974 /* Taken from camel-url-scanner.c */
4975 /* URLs are extremely unlikely to end with any punctuation, so
4976 * strip any trailing punctuation off from link and put it after
4977 * the link. Do the same for any closing double-quotes as well. */
4978 while (end_of_match
&& end_of_match
!= match
&& strchr (URL_INVALID_TRAILING_CHARS
, *end_of_match
)) {
4979 truncate_from_end
++;
4985 g_str_has_suffix (res
->str
, "<");
4987 if (link_surrounded
) {
4988 if (end_of_match
&& *end_of_match
&& strlen (match
) > strlen (end_of_match
) + 3)
4989 link_surrounded
= link_surrounded
&& g_str_has_prefix (end_of_match
- 3, ">");
4991 link_surrounded
= link_surrounded
&& g_str_has_suffix (match
, ">");
4993 if (link_surrounded
) {
4994 truncate_from_end
+= 4;
4999 /* The ending ';' was counted when looking for the invalid trailing characters, substract it. */
5000 if (link_surrounded
|| ending_with_nbsp
) {
5001 truncate_from_end
-= 1;
5005 /* If there is non-breaking space in the match, remove it and everything
5006 * after it from the match */
5007 if (!g_str_has_prefix (match
, " ") && !g_str_has_suffix (match
, " ") && (nbsp_match
= strstr (match
, " "))) {
5008 glong after_nbsp_length
= g_utf8_strlen (nbsp_match
, -1);
5009 truncate_from_end
= after_nbsp_length
;
5010 end_of_match
-= after_nbsp_length
;
5011 if (link_surrounded
)
5015 g_string_append (res
, "<a href=\"");
5016 if (strstr (match
, "@") && !strstr (match
, "://"))
5017 g_string_append (res
, "mailto:");
5018 g_string_append (res
, match
+ offset
);
5019 if (truncate_from_end
> 0)
5020 g_string_truncate (res
, res
->len
- truncate_from_end
);
5022 g_string_append (res
, "\">");
5023 g_string_append (res
, match
+ offset
);
5024 if (truncate_from_end
> 0)
5025 g_string_truncate (res
, res
->len
- truncate_from_end
);
5027 g_string_append (res
, "</a>");
5029 if (truncate_from_end
> 0)
5030 g_string_append (res
, end_of_match
);
5032 if (ending_with_nbsp
)
5033 g_string_append (res
, " ");
5041 replace_to_nbsp (const GMatchInfo
*info
,
5047 match
= g_match_info_fetch (info
, 0);
5049 while (match
[ii
] != '\0') {
5050 if (match
[ii
] == ' ') {
5051 /* Alone spaces or spaces before/after tabulator. */
5052 g_string_append (res
, " ");
5053 } else if (match
[ii
] == '\t') {
5054 /* Replace tabs with their WebKit HTML representation. */
5055 g_string_append (res
, "<span class=\"Apple-tab-span\" style=\"white-space:pre\">\t</span>");
5067 surround_links_with_anchor (const gchar
*text
)
5069 return (strstr (text
, "http") || strstr (text
, "ftp") ||
5070 strstr (text
, "www") || strstr (text
, "@"));
5074 append_new_block (WebKitDOMElement
*parent
,
5075 WebKitDOMElement
**block
)
5077 webkit_dom_node_append_child (
5078 WEBKIT_DOM_NODE (parent
),
5079 WEBKIT_DOM_NODE (*block
),
5085 static WebKitDOMElement
*
5086 create_and_append_new_block (EEditorPage
*editor_page
,
5087 WebKitDOMElement
*parent
,
5088 WebKitDOMElement
*block_template
,
5089 const gchar
*content
)
5091 WebKitDOMElement
*block
;
5093 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
5095 block
= WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node_with_error (
5096 WEBKIT_DOM_NODE (block_template
), FALSE
, NULL
));
5098 webkit_dom_element_set_inner_html (block
, content
, NULL
);
5100 append_new_block (parent
, &block
);
5106 append_citation_mark (WebKitDOMDocument
*document
,
5107 WebKitDOMElement
*parent
,
5108 const gchar
*citation_mark_text
)
5110 WebKitDOMText
*text
;
5112 text
= webkit_dom_document_create_text_node (document
, citation_mark_text
);
5114 webkit_dom_node_append_child (
5115 WEBKIT_DOM_NODE (parent
),
5116 WEBKIT_DOM_NODE (text
),
5121 replace_selection_markers (gchar
**text
)
5126 if (strstr (*text
, "##SELECTION_START##")) {
5129 tmp
= e_str_replace_string (
5131 "##SELECTION_START##",
5132 "<span id=\"-x-evo-selection-start-marker\"></span>");
5135 *text
= g_string_free (tmp
, FALSE
);
5138 if (strstr (*text
, "##SELECTION_END##")) {
5141 tmp
= e_str_replace_string (
5143 "##SELECTION_END##",
5144 "<span id=\"-x-evo-selection-end-marker\"></span>");
5147 *text
= g_string_free (tmp
, FALSE
);
5152 remove_new_lines_around_citations (const gchar
*input
)
5154 GString
*str
= NULL
;
5155 const gchar
*p
, *next
;
5157 str
= g_string_new ("");
5159 /* Remove the new lines around citations:
5160 * Replace <br><br>##CITATION_START## with <br>##CITATION_START##
5161 * Replace ##CITATION_START##<br><br> with ##CITATION_START##<br>
5162 * Replace ##CITATION_END##<br><br> with ##CITATION_END##<br>
5163 * Replace <br>##CITATION_END## with ##CITATION_END##
5164 * Replace <br>##CITATION_START## with ##CITATION_START## */
5166 while (next
= strstr (p
, "##CITATION_"), next
) {
5167 gchar citation_type
= 0;
5170 g_string_append_len (str
, p
, next
- p
);
5173 citation_type
= next
[11];
5174 /* ##CITATION_START## */
5175 if (citation_type
== 'S') {
5176 if (g_str_has_suffix (str
->str
, "<br><br>") ||
5177 g_str_has_suffix (str
->str
, "<br>"))
5178 g_string_truncate (str
, str
->len
- 4);
5180 if (g_str_has_prefix (next
+ 11, "START##<br><br>")) {
5181 g_string_append (str
, "##CITATION_START##<br>");
5185 } else if (citation_type
== 'E') {
5186 if (g_str_has_suffix (str
->str
, "<br>"))
5187 g_string_truncate (str
, str
->len
- 4);
5189 if (g_str_has_prefix (next
+ 11, "END##<br><br>")) {
5190 g_string_append (str
, "##CITATION_END##<br>");
5196 g_string_append (str
, "##CITATION_");
5201 g_string_append (str
, p
);
5203 if (camel_debug ("webkit:editor")) {
5204 printf ("EWebKitContentEditor - %s\n", G_STRFUNC
);
5205 printf ("\toutput: '%s'\n", str
->str
);
5212 replace_citation_marks_to_citations (const gchar
*input
)
5214 GString
*str
= NULL
;
5215 const gchar
*p
, *next
;
5217 str
= g_string_new ("");
5219 /* Replaces text markers with actual HTML blockquotes */
5221 while (next
= strstr (p
, "##CITATION_"), next
) {
5222 gchar citation_type
= 0;
5225 g_string_append_len (str
, p
, next
- p
);
5228 citation_type
= next
[11];
5229 /* ##CITATION_START## */
5230 if (citation_type
== 'S') {
5231 g_string_append (str
, "<blockquote type=\"cite\">");
5233 } else if (citation_type
== 'E') {
5234 g_string_append (str
, "</blockquote>");
5240 g_string_append (str
, p
);
5245 /* This parses the HTML code (that contains just text, and BR elements)
5247 * HTML code in that format we can get by taking innerText from some element,
5248 * setting it to another one and finally getting innerHTML from it */
5250 parse_html_into_blocks (EEditorPage
*editor_page
,
5251 WebKitDOMElement
*parent
,
5252 WebKitDOMElement
*passed_block_template
,
5255 gboolean has_citation
= FALSE
, processing_last
= FALSE
;
5256 const gchar
*prev_token
, *next_token
;
5257 const gchar
*next_br_token
= NULL
, *next_citation_token
= NULL
;
5258 GString
*html
= NULL
;
5259 GRegex
*regex_nbsp
= NULL
, *regex_link
= NULL
, *regex_email
= NULL
;
5260 WebKitDOMDocument
*document
;
5261 WebKitDOMElement
*block_template
= passed_block_template
;
5263 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5265 if (!(input
&& *input
))
5268 document
= e_editor_page_get_document (editor_page
);
5269 webkit_dom_element_set_inner_html (parent
, "", NULL
);
5271 if (!block_template
) {
5272 gboolean use_paragraphs
;
5273 GSettings
*settings
;
5275 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
5277 use_paragraphs
= g_settings_get_boolean (
5278 settings
, "composer-wrap-quoted-text-in-replies");
5281 block_template
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
5283 block_template
= webkit_dom_document_create_element (document
, "pre", NULL
);
5285 g_object_unref (settings
);
5288 /* Replace the tabulators with SPAN elements that corresponds to them.
5289 * If not inserting the content into the PRE element also replace single
5290 * spaces on the beginning of line, 2+ spaces and with non breaking
5292 if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block_template
))
5293 regex_nbsp
= g_regex_new ("\x9", 0, 0, NULL
);
5295 regex_nbsp
= g_regex_new ("^\\s{1}|\\s{2,}|\x9|\\s$", 0, 0, NULL
);
5297 if (camel_debug ("webkit:editor")) {
5298 printf ("EWebKitContentEditor - %s\n", G_STRFUNC
);
5299 printf ("\tinput: '%s'\n", input
);
5301 html
= remove_new_lines_around_citations (input
);
5303 prev_token
= html
->str
;
5304 next_br_token
= (prev_token
&& *prev_token
) ? strstr (prev_token
+ 1, "<br>") : NULL
;
5305 next_citation_token
= (prev_token
&& *prev_token
) ? strstr (prev_token
+ 1, "##CITATION_") : NULL
;
5306 if (next_br_token
) {
5307 if (next_citation_token
)
5308 next_token
= next_br_token
< next_citation_token
? next_br_token
: next_citation_token
;
5310 next_token
= next_br_token
;
5312 next_token
= next_citation_token
;
5314 processing_last
= !next_token
;
5316 while (next_token
|| processing_last
) {
5317 const gchar
*citation_start
= NULL
, *citation_end
= NULL
;
5318 const gchar
*rest
= NULL
, *with_br
= NULL
;
5319 gchar
*to_process
= NULL
, *to_insert
= NULL
;
5320 guint to_insert_start
= 0, to_insert_end
= 0;
5323 to_process
= g_strdup (prev_token
);
5324 processing_last
= TRUE
;
5325 } else if ((to_process
= g_utf8_substring (prev_token
, 0, g_utf8_pointer_to_offset (prev_token
, next_token
))) &&
5326 !*to_process
&& !processing_last
) {
5327 g_free (to_process
);
5328 to_process
= g_strdup (next_token
);
5330 processing_last
= TRUE
;
5333 if (camel_debug ("webkit:editor"))
5334 printf ("\tto_process: '%s'\n", to_process
);
5336 if (to_process
&& !*to_process
&& processing_last
) {
5337 g_free (to_process
);
5338 to_process
= g_strdup (next_token
);
5342 to_insert_end
= g_utf8_strlen (to_process
, -1);
5344 if ((with_br
= strstr (to_process
, "<br>"))) {
5345 if (with_br
== to_process
)
5346 to_insert_start
+= 4;
5349 if ((citation_start
= strstr (to_process
, "##CITATION_START"))) {
5350 if (with_br
&& citation_start
== with_br
+ 4)
5351 to_insert_start
+= 18; /* + ## */
5352 else if (!with_br
&& citation_start
== to_process
)
5353 to_insert_start
+= 18; /* + ## */
5355 to_insert_end
-= 18; /* + ## */
5356 has_citation
= TRUE
;
5359 if ((citation_end
= strstr (to_process
, "##CITATION_END"))) {
5360 if (citation_end
== to_process
)
5361 to_insert_start
+= 16;
5363 to_insert_end
-= 16; /* + ## */
5367 if (with_br
&& prev_token
== html
->str
)
5368 create_and_append_new_block (
5369 editor_page
, parent
, block_template
, "<br id=\"-x-evo-first-br\">");
5371 if (with_br
&& citation_start
&& citation_start
== with_br
+ 4) {
5372 create_and_append_new_block (
5373 editor_page
, parent
, block_template
, "<br>");
5375 append_citation_mark (document
, parent
, "##CITATION_START##");
5376 } else if (!with_br
&& citation_start
== to_process
) {
5377 append_citation_mark (document
, parent
, "##CITATION_START##");
5380 if (citation_end
&& citation_end
== to_process
) {
5381 append_citation_mark (document
, parent
, "##CITATION_END##");
5384 if ((to_insert
= g_utf8_substring (to_process
, to_insert_start
, to_insert_end
)) && *to_insert
) {
5385 gboolean empty
= FALSE
;
5386 gchar
*truncated
= g_strdup (to_insert
);
5387 gchar
*rest_to_insert
;
5389 if (camel_debug ("webkit:editor"))
5390 printf ("\tto_insert: '%s'\n", to_insert
);
5392 empty
= !*truncated
&& strlen (to_insert
) > 0;
5394 rest_to_insert
= g_regex_replace_eval (
5396 empty
? rest
: truncated
,
5400 (GRegexEvalCallback
) replace_to_nbsp
,
5405 replace_selection_markers (&rest_to_insert
);
5407 if (surround_links_with_anchor (rest_to_insert
)) {
5408 gboolean is_email_address
=
5409 strstr (rest_to_insert
, "@") &&
5410 !strstr (rest_to_insert
, "://");
5412 if (is_email_address
&& !regex_email
)
5413 regex_email
= g_regex_new (E_MAIL_PATTERN
, 0, 0, NULL
);
5414 if (!is_email_address
&& !regex_link
)
5415 regex_link
= g_regex_new (URL_PATTERN
, 0, 0, NULL
);
5417 truncated
= g_regex_replace_eval (
5418 is_email_address
? regex_email
: regex_link
,
5422 G_REGEX_MATCH_NOTEMPTY
,
5423 create_anchor_for_link
,
5427 g_free (rest_to_insert
);
5428 rest_to_insert
= truncated
;
5431 create_and_append_new_block (
5432 editor_page
, parent
, block_template
, rest_to_insert
);
5434 g_free (rest_to_insert
);
5435 } else if (to_insert
) {
5436 if (!citation_start
&& (with_br
|| !citation_end
))
5437 create_and_append_new_block (
5438 editor_page
, parent
, block_template
, "<br>");
5439 else if (citation_end
&& citation_end
== to_process
&&
5440 next_token
&& g_str_has_prefix (next_token
, "<br>")) {
5441 create_and_append_new_block (
5442 editor_page
, parent
, block_template
, "<br>");
5448 if (with_br
&& citation_start
&& citation_start
!= with_br
+ 4)
5449 append_citation_mark (document
, parent
, "##CITATION_START##");
5451 if (!with_br
&& citation_start
&& citation_start
!= to_process
)
5452 append_citation_mark (document
, parent
, "##CITATION_START##");
5454 if (citation_end
&& citation_end
!= to_process
)
5455 append_citation_mark (document
, parent
, "##CITATION_END##");
5457 g_free (to_process
);
5459 prev_token
= next_token
;
5460 next_br_token
= (prev_token
&& *prev_token
) ? strstr (prev_token
+ 1, "<br>") : NULL
;
5461 next_citation_token
= (prev_token
&& *prev_token
) ? strstr (prev_token
+ 1, "##CITATION_") : NULL
;
5462 if (next_br_token
) {
5463 if (next_citation_token
)
5464 next_token
= next_br_token
< next_citation_token
? next_br_token
: next_citation_token
;
5466 next_token
= next_br_token
;
5468 next_token
= next_citation_token
;
5471 if (!next_token
&& !processing_last
) {
5475 if (g_utf8_strlen (prev_token
, -1) > 4) {
5476 next_token
= prev_token
;
5478 WebKitDOMNode
*child
;
5480 if (g_strcmp0 (prev_token
, "<br>") == 0)
5481 create_and_append_new_block (
5482 editor_page
, parent
, block_template
, "<br>");
5484 child
= webkit_dom_node_get_last_child (
5485 WEBKIT_DOM_NODE (parent
));
5487 child
= webkit_dom_node_get_first_child (child
);
5488 if (child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
)) {
5489 /* If the processed HTML contained just
5490 * the BR don't overwrite its id. */
5491 if (!element_has_id (WEBKIT_DOM_ELEMENT (child
), "-x-evo-first-br"))
5492 webkit_dom_element_set_id (
5493 WEBKIT_DOM_ELEMENT (child
),
5497 create_and_append_new_block (
5498 editor_page
, parent
, block_template
, "<br>");
5502 processing_last
= TRUE
;
5503 } else if (processing_last
&& !prev_token
&& !next_token
) {
5512 /* Replace text markers with actual HTML blockquotes */
5513 inner_html
= webkit_dom_element_get_inner_html (parent
);
5514 parsed
= replace_citation_marks_to_citations (inner_html
);
5515 webkit_dom_element_set_inner_html (parent
, parsed
->str
, NULL
);
5517 if (camel_debug ("webkit:editor"))
5518 printf ("\tparsed content: '%s'\n", inner_html
);
5520 g_free (inner_html
);
5521 g_string_free (parsed
, TRUE
);
5522 } else if (camel_debug ("webkit:editor")) {
5525 inner_html
= webkit_dom_element_get_inner_html (parent
);
5526 printf ("\tparsed content: '%s'\n", inner_html
);
5527 g_free (inner_html
);
5530 g_string_free (html
, TRUE
);
5532 if (regex_email
!= NULL
)
5533 g_regex_unref (regex_email
);
5534 if (regex_link
!= NULL
)
5535 g_regex_unref (regex_link
);
5536 g_regex_unref (regex_nbsp
);
5540 e_editor_dom_quote_and_insert_text_into_selection (EEditorPage
*editor_page
,
5544 WebKitDOMDocument
*document
;
5545 WebKitDOMElement
*blockquote
, *element
, *selection_start
;
5546 WebKitDOMNode
*node
;
5547 EEditorHistoryEvent
*ev
= NULL
;
5548 EEditorUndoRedoManager
*manager
;
5550 gboolean node_added
= FALSE
;
5552 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5554 if (!text
|| !*text
)
5557 document
= e_editor_page_get_document (editor_page
);
5560 element
= webkit_dom_document_create_element (document
, "div", NULL
);
5561 webkit_dom_element_set_inner_html (element
, text
, NULL
);
5563 /* This is a trick to escape any HTML characters (like <, > or &).
5564 * <textarea> automatically replaces all these unsafe characters
5565 * by <, > etc. */
5566 element
= webkit_dom_document_create_element (document
, "textarea", NULL
);
5567 webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element
), text
, NULL
);
5570 inner_html
= webkit_dom_element_get_inner_html (element
);
5572 e_editor_dom_selection_save (editor_page
);
5574 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
5575 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
5576 ev
= g_new0 (EEditorHistoryEvent
, 1);
5577 ev
->type
= HISTORY_PASTE_QUOTED
;
5579 e_editor_dom_selection_get_coordinates (editor_page
,
5580 &ev
->before
.start
.x
,
5581 &ev
->before
.start
.y
,
5585 ev
->data
.string
.from
= NULL
;
5586 ev
->data
.string
.to
= g_strdup (text
);
5589 blockquote
= webkit_dom_document_create_element (document
, "blockquote", NULL
);
5590 webkit_dom_element_set_attribute (blockquote
, "type", "cite", NULL
);
5592 selection_start
= webkit_dom_document_get_element_by_id (
5593 document
, "-x-evo-selection-start-marker");
5594 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start
));
5596 /* Check if block is empty. If so, replace it otherwise insert the quoted
5597 * content after current block. */
5598 if (!node
|| WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
5599 node
= webkit_dom_node_get_next_sibling (
5600 WEBKIT_DOM_NODE (selection_start
));
5601 node
= webkit_dom_node_get_next_sibling (node
);
5602 if (!node
|| WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
5603 webkit_dom_node_replace_child (
5604 webkit_dom_node_get_parent_node (
5605 webkit_dom_node_get_parent_node (
5606 WEBKIT_DOM_NODE (selection_start
))),
5607 WEBKIT_DOM_NODE (blockquote
),
5608 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start
)),
5615 WebKitDOMNode
*parent
, *next_sibling
= NULL
;
5617 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start
));
5618 next_sibling
= webkit_dom_node_get_next_sibling (parent
);
5620 if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent
)) {
5621 WebKitDOMNode
*up_parent
;
5623 up_parent
= webkit_dom_node_get_parent_node (parent
);
5624 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (up_parent
)) {
5630 webkit_dom_node_insert_before (
5631 webkit_dom_node_get_parent_node (next_sibling
),
5632 WEBKIT_DOM_NODE (blockquote
),
5636 webkit_dom_node_append_child (
5638 WEBKIT_DOM_NODE (blockquote
),
5643 parse_html_into_blocks (editor_page
, blockquote
, NULL
, inner_html
);
5645 if (e_editor_page_get_html_mode (editor_page
)) {
5646 node
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (blockquote
));
5648 gint word_wrap_length
;
5650 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
5651 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote
));
5653 WebKitDOMNode
*next_sibling
;
5655 if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (node
))
5656 node
= WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (editor_page
, WEBKIT_DOM_ELEMENT (node
), word_wrap_length
- 2));
5658 webkit_dom_node_normalize (node
);
5659 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (node
),
5660 e_editor_dom_get_citation_level (node
));
5662 next_sibling
= webkit_dom_node_get_next_sibling (node
);
5666 node
= next_sibling
;
5670 dom_add_selection_markers_into_element_end (
5671 document
, WEBKIT_DOM_ELEMENT (node
), NULL
, NULL
);
5673 e_editor_dom_selection_restore (editor_page
);
5676 e_editor_dom_selection_get_coordinates (editor_page
,
5681 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
5684 if ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-first-br")))
5685 webkit_dom_element_remove_attribute (element
, "id");
5686 if ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-last-br")))
5687 webkit_dom_element_remove_attribute (element
, "id");
5689 e_editor_dom_force_spell_check_in_viewport (editor_page
);
5690 e_editor_page_emit_content_changed (editor_page
);
5692 g_free (inner_html
);
5696 mark_citation (WebKitDOMElement
*citation
)
5698 webkit_dom_element_insert_adjacent_text (
5701 "##CITATION_START##",
5704 webkit_dom_element_insert_adjacent_text (
5710 element_add_class (citation
, "marked");
5714 create_text_markers_for_citations_in_element (WebKitDOMElement
*element
)
5717 WebKitDOMElement
*citation
;
5719 citation
= webkit_dom_element_query_selector (
5720 element
, "blockquote[type=cite]:not(.marked)", NULL
);
5723 mark_citation (citation
);
5726 citation
= webkit_dom_element_query_selector (
5727 element
, "blockquote[type=cite]:not(.marked)", NULL
);
5734 create_text_markers_for_selection_in_element (WebKitDOMElement
*element
)
5736 WebKitDOMElement
*selection_marker
;
5738 selection_marker
= webkit_dom_element_query_selector (
5739 element
, "#-x-evo-selection-start-marker", NULL
);
5740 if (selection_marker
)
5741 webkit_dom_element_insert_adjacent_text (
5744 "##SELECTION_START##",
5747 selection_marker
= webkit_dom_element_query_selector (
5748 element
, "#-x-evo-selection-end-marker", NULL
);
5749 if (selection_marker
)
5750 webkit_dom_element_insert_adjacent_text (
5753 "##SELECTION_END##",
5758 quote_plain_text_elements_after_wrapping_in_element (EEditorPage
*editor_page
,
5759 WebKitDOMElement
*element
)
5761 WebKitDOMNodeList
*list
= NULL
;
5764 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5766 /* Also quote the PRE elements as well. */
5767 list
= webkit_dom_element_query_selector_all (
5768 element
, "blockquote[type=cite] > [data-evo-paragraph], blockquote[type=cite] > pre", NULL
);
5770 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
5771 gint citation_level
;
5772 WebKitDOMNode
*child
;
5774 child
= webkit_dom_node_list_item (list
, ii
);
5775 citation_level
= e_editor_dom_get_citation_level (child
);
5776 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (child
), citation_level
);
5778 g_clear_object (&list
);
5782 quote_plain_text_elements_after_wrapping_in_document (EEditorPage
*editor_page
)
5784 WebKitDOMDocument
*document
;
5785 WebKitDOMHTMLElement
*body
;
5787 document
= e_editor_page_get_document (editor_page
);
5788 body
= webkit_dom_document_get_body (document
);
5790 quote_plain_text_elements_after_wrapping_in_element (editor_page
, WEBKIT_DOM_ELEMENT (body
));
5794 clear_attributes (EEditorPage
*editor_page
)
5796 WebKitDOMDocument
*document
;
5797 WebKitDOMNamedNodeMap
*attributes
= NULL
;
5798 WebKitDOMHTMLElement
*body
;
5799 WebKitDOMHTMLHeadElement
*head
;
5800 WebKitDOMElement
*document_element
;
5803 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5805 document
= e_editor_page_get_document (editor_page
);
5806 body
= webkit_dom_document_get_body (document
);
5807 head
= webkit_dom_document_get_head (document
);
5808 document_element
= webkit_dom_document_get_document_element (document
);
5810 /* Remove all attributes from HTML element */
5811 attributes
= webkit_dom_element_get_attributes (document_element
);
5812 length
= webkit_dom_named_node_map_get_length (attributes
);
5813 for (ii
= length
- 1; ii
>= 0; ii
--)
5814 webkit_dom_element_remove_attribute_node (
5816 WEBKIT_DOM_ATTR (webkit_dom_named_node_map_item (attributes
, ii
)),
5818 g_clear_object (&attributes
);
5820 /* Remove everything from HEAD element */
5821 while (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head
)))
5822 remove_node (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head
)));
5824 /* Make the quote marks non-selectable. */
5825 e_editor_dom_disable_quote_marks_select (editor_page
);
5827 /* Remove non Evolution attributes from BODY element */
5828 attributes
= webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body
));
5829 length
= webkit_dom_named_node_map_get_length (attributes
);
5830 for (ii
= length
- 1; ii
>= 0; ii
--) {
5832 WebKitDOMAttr
*attribute
= WEBKIT_DOM_ATTR (webkit_dom_named_node_map_item (attributes
, ii
));
5834 name
= webkit_dom_attr_get_name (attribute
);
5836 if (!g_str_has_prefix (name
, "data-") && (g_strcmp0 (name
, "spellcheck") != 0))
5837 webkit_dom_element_remove_attribute_node (
5838 WEBKIT_DOM_ELEMENT (body
), attribute
, NULL
);
5842 g_clear_object (&attributes
);
5846 body_compositionstart_event_cb (WebKitDOMElement
*element
,
5847 WebKitDOMUIEvent
*event
,
5848 EEditorPage
*editor_page
)
5850 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5852 e_editor_page_set_composition_in_progress (editor_page
, TRUE
);
5853 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
5857 body_compositionend_event_cb (WebKitDOMElement
*element
,
5858 WebKitDOMUIEvent
*event
,
5859 EEditorPage
*editor_page
)
5861 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5863 e_editor_page_set_composition_in_progress (editor_page
, FALSE
);
5864 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
5868 body_drop_event_cb (WebKitDOMElement
*element
,
5869 WebKitDOMUIEvent
*dom_ui_event
,
5870 EEditorPage
*editor_page
)
5872 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5874 if (e_editor_page_is_pasting_content_from_itself (editor_page
)) {
5875 EEditorUndoRedoManager
*manager
;
5876 EEditorHistoryEvent
*and_event
, *event
= NULL
;
5878 /* There is a weird thing going on and I still don't know if it's
5879 * caused by WebKit or Evolution. If dragging content around the
5880 * editor sometimes the current selection is changed. The problem
5881 * is that if moving the content, then WebKit is removing the
5882 * currently selected content and at that point it could be a
5883 * different one from the dragged one. So before the drop is
5884 * performed we restore the selection to the state when the
5885 * drag was initiated. */
5886 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
5887 and_event
= e_editor_undo_redo_manager_get_current_history_event (manager
);
5888 while (and_event
&& and_event
->type
== HISTORY_AND
) {
5889 event
= e_editor_undo_redo_manager_get_next_history_event_for (manager
, and_event
);
5890 and_event
= e_editor_undo_redo_manager_get_next_history_event_for (manager
, event
);
5894 e_editor_dom_selection_restore_to_history_event_state (editor_page
, event
->before
);
5896 e_editor_dom_save_history_for_drop (editor_page
);
5901 body_dragstart_event_cb (WebKitDOMElement
*element
,
5902 WebKitDOMUIEvent
*event
,
5903 EEditorPage
*editor_page
)
5905 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5907 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
5908 e_editor_page_set_pasting_content_from_itself (editor_page
, TRUE
);
5909 e_editor_dom_save_history_for_drag (editor_page
);
5913 body_dragend_event_cb (WebKitDOMElement
*element
,
5914 WebKitDOMUIEvent
*event
,
5915 EEditorPage
*editor_page
)
5917 EEditorHistoryEvent
*ev
;
5918 EEditorUndoRedoManager
*manager
;
5920 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5922 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
5923 if (e_editor_page_is_pasting_content_from_itself (editor_page
) &&
5924 (ev
= e_editor_undo_redo_manager_get_current_history_event (manager
))) {
5925 if (ev
->type
== HISTORY_INSERT_HTML
&&
5926 ev
->after
.start
.x
== 0 && ev
->after
.start
.y
== 0 &&
5927 ev
->after
.end
.x
== 0 && ev
->after
.end
.y
== 0) {
5928 e_editor_dom_selection_get_coordinates (editor_page
,
5933 ev
->before
.start
.x
= ev
->after
.start
.x
;
5934 ev
->before
.start
.y
= ev
->after
.start
.y
;
5935 ev
->before
.end
.x
= ev
->after
.start
.x
;
5936 ev
->before
.end
.y
= ev
->after
.start
.y
;
5937 e_editor_dom_force_spell_check_in_viewport (editor_page
);
5939 /* Drag and Drop was cancelled */
5940 while (ev
&& ev
->type
== HISTORY_AND
) {
5941 e_editor_undo_redo_manager_remove_current_history_event (manager
);
5942 ev
= e_editor_undo_redo_manager_get_current_history_event (manager
);
5943 /* Basically the same as in body_drop_event_cb(). See the comment there. */
5944 e_editor_dom_selection_restore_to_history_event_state (editor_page
, ev
->before
);
5945 e_editor_undo_redo_manager_remove_current_history_event (manager
);
5946 ev
= e_editor_undo_redo_manager_get_current_history_event (manager
);
5951 e_editor_page_set_pasting_content_from_itself (editor_page
, FALSE
);
5952 e_editor_dom_register_input_event_listener_on_body (editor_page
);
5956 register_html_events_handlers (EEditorPage
*editor_page
,
5957 WebKitDOMHTMLElement
*body
)
5959 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
5961 webkit_dom_event_target_add_event_listener (
5962 WEBKIT_DOM_EVENT_TARGET (body
),
5964 G_CALLBACK (body_keydown_event_cb
),
5968 webkit_dom_event_target_add_event_listener (
5969 WEBKIT_DOM_EVENT_TARGET (body
),
5971 G_CALLBACK (body_keypress_event_cb
),
5975 webkit_dom_event_target_add_event_listener (
5976 WEBKIT_DOM_EVENT_TARGET (body
),
5978 G_CALLBACK (body_keyup_event_cb
),
5982 webkit_dom_event_target_add_event_listener (
5983 WEBKIT_DOM_EVENT_TARGET (body
),
5985 G_CALLBACK (body_compositionstart_event_cb
),
5989 webkit_dom_event_target_add_event_listener (
5990 WEBKIT_DOM_EVENT_TARGET (body
),
5992 G_CALLBACK (body_compositionend_event_cb
),
5996 webkit_dom_event_target_add_event_listener (
5997 WEBKIT_DOM_EVENT_TARGET (body
),
5999 G_CALLBACK (body_drop_event_cb
),
6003 webkit_dom_event_target_add_event_listener (
6004 WEBKIT_DOM_EVENT_TARGET (body
),
6006 G_CALLBACK (body_dragstart_event_cb
),
6010 webkit_dom_event_target_add_event_listener (
6011 WEBKIT_DOM_EVENT_TARGET (body
),
6013 G_CALLBACK (body_dragend_event_cb
),
6019 e_editor_dom_convert_content (EEditorPage
*editor_page
,
6020 const gchar
*preferred_text
,
6021 gint16 in_start_at_bottom
,
6022 gint16 in_top_signature
)
6024 WebKitDOMDocument
*document
;
6025 WebKitDOMElement
*paragraph
, *content_wrapper
, *top_signature
;
6026 WebKitDOMElement
*cite_body_element
, *signature
, *wrapper
;
6027 WebKitDOMHTMLElement
*body
;
6028 WebKitDOMNodeList
*list
= NULL
;
6029 WebKitDOMNode
*node
;
6030 WebKitDOMDOMWindow
*dom_window
= NULL
;
6031 gboolean start_bottom
, empty
= FALSE
, cite_body
= FALSE
;
6033 gint ii
, jj
, length
;
6034 GSettings
*settings
;
6036 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
6038 document
= e_editor_page_get_document (editor_page
);
6039 if (in_start_at_bottom
== 0 || in_start_at_bottom
== 1) {
6040 start_bottom
= in_start_at_bottom
== 1;
6042 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
6043 start_bottom
= g_settings_get_boolean (settings
, "composer-reply-start-bottom");
6044 g_object_unref (settings
);
6047 dom_window
= webkit_dom_document_get_default_view (document
);
6048 body
= webkit_dom_document_get_body (document
);
6049 /* Wrapper that will represent the new body. */
6050 wrapper
= webkit_dom_document_create_element (document
, "div", NULL
);
6052 cite_body_element
= webkit_dom_document_query_selector (
6053 document
, "span.-x-evo-cite-body", NULL
);
6055 /* content_wrapper when the processed text will be placed. */
6056 content_wrapper
= webkit_dom_document_create_element (
6057 document
, cite_body_element
? "blockquote" : "div", NULL
);
6058 if (cite_body_element
) {
6060 webkit_dom_element_set_attribute (content_wrapper
, "type", "cite", NULL
);
6061 webkit_dom_element_set_attribute (content_wrapper
, "id", "-x-evo-main-cite", NULL
);
6062 remove_node (WEBKIT_DOM_NODE (cite_body_element
));
6065 webkit_dom_node_append_child (
6066 WEBKIT_DOM_NODE (wrapper
), WEBKIT_DOM_NODE (content_wrapper
), NULL
);
6068 /* Remove all previously inserted paragraphs. */
6069 list
= webkit_dom_document_query_selector_all (
6070 document
, "[data-evo-paragraph]:not([data-headers])", NULL
);
6071 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;)
6072 remove_node (webkit_dom_node_list_item (list
, ii
));
6073 g_clear_object (&list
);
6075 /* Insert the paragraph where the caret will be. */
6076 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
6077 webkit_dom_element_set_id (paragraph
, "-x-evo-input-start");
6078 webkit_dom_node_insert_before (
6079 WEBKIT_DOM_NODE (wrapper
),
6080 WEBKIT_DOM_NODE (paragraph
),
6082 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (content_wrapper
)) :
6083 WEBKIT_DOM_NODE (content_wrapper
),
6086 /* Insert signature (if presented) to the right position. */
6087 top_signature
= webkit_dom_document_query_selector (
6088 document
, ".-x-evo-top-signature", NULL
);
6089 signature
= webkit_dom_document_query_selector (
6090 document
, ".-x-evo-signature-wrapper", NULL
);
6092 if (top_signature
) {
6093 WebKitDOMElement
*spacer
;
6095 webkit_dom_node_insert_before (
6096 WEBKIT_DOM_NODE (wrapper
),
6097 WEBKIT_DOM_NODE (signature
),
6099 WEBKIT_DOM_NODE (content_wrapper
) :
6100 webkit_dom_node_get_next_sibling (
6101 WEBKIT_DOM_NODE (paragraph
)),
6103 /* Insert NL after the signature */
6104 spacer
= e_editor_dom_prepare_paragraph (editor_page
, FALSE
);
6105 element_add_class (spacer
, "-x-evo-top-signature-spacer");
6106 webkit_dom_node_insert_before (
6107 WEBKIT_DOM_NODE (wrapper
),
6108 WEBKIT_DOM_NODE (spacer
),
6109 webkit_dom_node_get_next_sibling (
6110 WEBKIT_DOM_NODE (signature
)),
6113 webkit_dom_node_insert_before (
6114 WEBKIT_DOM_NODE (wrapper
),
6115 WEBKIT_DOM_NODE (signature
),
6116 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (
6117 start_bottom
? paragraph
: content_wrapper
)),
6122 /* Move credits to the body */
6123 list
= webkit_dom_document_query_selector_all (
6124 document
, "span.-x-evo-to-body[data-credits]", NULL
);
6125 e_editor_page_set_allow_top_signature (editor_page
, webkit_dom_node_list_get_length (list
) > 0);
6126 for (jj
= 0, ii
= webkit_dom_node_list_get_length (list
); ii
--; jj
++) {
6128 WebKitDOMElement
*element
;
6129 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, jj
);
6131 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
6132 credits
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "data-credits");
6134 webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element
), credits
, NULL
);
6137 webkit_dom_node_insert_before (
6138 WEBKIT_DOM_NODE (wrapper
),
6139 WEBKIT_DOM_NODE (element
),
6140 WEBKIT_DOM_NODE (content_wrapper
),
6145 g_clear_object (&list
);
6147 /* Move headers to body */
6148 list
= webkit_dom_document_query_selector_all (
6149 document
, "div[data-headers]", NULL
);
6150 for (jj
= 0, ii
= webkit_dom_node_list_get_length (list
); ii
--; jj
++) {
6151 WebKitDOMNode
*node
;
6153 node
= webkit_dom_node_list_item (list
, jj
);
6154 webkit_dom_element_remove_attribute (
6155 WEBKIT_DOM_ELEMENT (node
), "data-headers");
6156 e_editor_dom_set_paragraph_style (editor_page
, WEBKIT_DOM_ELEMENT (node
), -1, 0, NULL
);
6157 webkit_dom_node_insert_before (
6158 WEBKIT_DOM_NODE (wrapper
),
6160 WEBKIT_DOM_NODE (content_wrapper
),
6163 g_clear_object (&list
);
6165 repair_blockquotes (document
);
6166 remove_thunderbird_signature (document
);
6167 create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (body
));
6169 if (preferred_text
&& *preferred_text
)
6170 webkit_dom_html_element_set_inner_text (
6171 WEBKIT_DOM_HTML_ELEMENT (content_wrapper
), preferred_text
, NULL
);
6174 WebKitDOMNode
*last_child
;
6176 inner_text
= webkit_dom_html_element_get_inner_text (body
);
6177 webkit_dom_html_element_set_inner_text (
6178 WEBKIT_DOM_HTML_ELEMENT (content_wrapper
), inner_text
, NULL
);
6180 last_child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (content_wrapper
));
6181 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child
))
6182 remove_node (last_child
);
6184 g_free (inner_text
);
6187 inner_html
= webkit_dom_element_get_inner_html (content_wrapper
);
6189 /* Replace the old body with the new one. */
6190 node
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body
), FALSE
, NULL
);
6191 webkit_dom_node_replace_child (
6192 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body
)),
6194 WEBKIT_DOM_NODE (body
),
6196 body
= WEBKIT_DOM_HTML_ELEMENT (node
);
6198 /* Copy all to nodes to the new body. */
6199 while ((node
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (wrapper
)))) {
6200 webkit_dom_node_insert_before (
6201 WEBKIT_DOM_NODE (body
),
6202 WEBKIT_DOM_NODE (node
),
6203 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)),
6207 if (inner_html
&& !*inner_html
)
6210 remove_node (WEBKIT_DOM_NODE (wrapper
));
6212 length
= webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (body
));
6216 WebKitDOMNode
*child
;
6218 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
6219 empty
= child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
);
6223 if (preferred_text
&& *preferred_text
)
6227 parse_html_into_blocks (editor_page
, content_wrapper
, NULL
, inner_html
);
6229 webkit_dom_node_append_child (
6230 WEBKIT_DOM_NODE (content_wrapper
),
6231 WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page
, FALSE
)),
6236 WebKitDOMNode
*child
;
6238 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (content_wrapper
)))) {
6239 webkit_dom_node_insert_before (
6240 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (content_wrapper
)),
6242 WEBKIT_DOM_NODE (content_wrapper
),
6247 remove_node (WEBKIT_DOM_NODE (content_wrapper
));
6250 /* If not editing a message, don't add any new block and just place
6251 * the caret in the beginning of content. We want to have the same
6252 * behaviour when editing message as new or we start replying on top. */
6253 if (!signature
&& !start_bottom
) {
6254 WebKitDOMNode
*child
;
6256 remove_node (WEBKIT_DOM_NODE (paragraph
));
6257 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
6259 dom_add_selection_markers_into_element_start (
6260 document
, WEBKIT_DOM_ELEMENT (child
), NULL
, NULL
);
6263 if ((paragraph
= webkit_dom_document_get_element_by_id (document
, "-x-evo-first-br")))
6264 webkit_dom_element_remove_attribute (paragraph
, "id");
6265 if ((paragraph
= webkit_dom_document_get_element_by_id (document
, "-x-evo-last-br")))
6266 webkit_dom_element_remove_attribute (paragraph
, "id");
6268 e_editor_dom_merge_siblings_if_necessary (editor_page
, NULL
);
6270 if (!e_editor_page_get_html_mode (editor_page
)) {
6271 e_editor_dom_wrap_paragraphs_in_document (editor_page
);
6273 quote_plain_text_elements_after_wrapping_in_document (editor_page
);
6276 clear_attributes (editor_page
);
6278 e_editor_dom_selection_restore (editor_page
);
6279 e_editor_dom_force_spell_check_in_viewport (editor_page
);
6281 /* Register on input event that is called when the content (body) is modified */
6282 webkit_dom_event_target_add_event_listener (
6283 WEBKIT_DOM_EVENT_TARGET (body
),
6285 G_CALLBACK (body_input_event_cb
),
6289 webkit_dom_event_target_add_event_listener (
6290 WEBKIT_DOM_EVENT_TARGET (dom_window
),
6292 G_CALLBACK (body_scroll_event_cb
),
6296 /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the
6297 * callback won't be set up. */
6299 register_html_events_handlers (editor_page
, body
);
6301 g_free (inner_html
);
6305 preserve_line_breaks_in_element (WebKitDOMDocument
*document
,
6306 WebKitDOMElement
*element
,
6307 const gchar
*selector
)
6309 WebKitDOMNodeList
*list
= NULL
;
6312 if (!(list
= webkit_dom_element_query_selector_all (element
, selector
, NULL
)))
6315 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
6316 gboolean insert
= TRUE
;
6317 WebKitDOMNode
*node
, *next_sibling
;
6319 node
= webkit_dom_node_list_item (list
, ii
);
6320 next_sibling
= webkit_dom_node_get_next_sibling (node
);
6325 while (insert
&& next_sibling
) {
6326 if (!webkit_dom_node_has_child_nodes (next_sibling
) &&
6327 !webkit_dom_node_get_next_sibling (next_sibling
))
6329 next_sibling
= webkit_dom_node_get_next_sibling (next_sibling
);
6332 if (insert
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node
)))
6333 webkit_dom_node_append_child (
6335 WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)),
6338 g_clear_object (&list
);
6342 preserve_pre_line_breaks_in_element (WebKitDOMDocument
*document
,
6343 WebKitDOMElement
*element
)
6345 WebKitDOMHTMLCollection
*collection
= NULL
;
6348 if (!(collection
= webkit_dom_element_get_elements_by_tag_name_as_html_collection (element
, "pre")))
6351 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
6352 WebKitDOMNode
*node
;
6356 node
= webkit_dom_html_collection_item (collection
, ii
);
6357 inner_html
= webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (node
));
6358 string
= e_str_replace_string (inner_html
, "\n", "<br>");
6359 webkit_dom_element_set_inner_html (WEBKIT_DOM_ELEMENT (node
), string
->str
, NULL
);
6360 g_string_free (string
, TRUE
);
6361 g_free (inner_html
);
6363 g_clear_object (&collection
);
6367 e_editor_dom_convert_and_insert_html_into_selection (EEditorPage
*editor_page
,
6371 WebKitDOMDocument
*document
;
6372 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *element
;
6373 WebKitDOMNode
*node
, *current_block
;
6374 EEditorHistoryEvent
*ev
= NULL
;
6375 EEditorUndoRedoManager
*manager
;
6376 gboolean has_selection
;
6378 gint citation_level
;
6380 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
6382 document
= e_editor_page_get_document (editor_page
);
6384 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
6386 e_editor_dom_selection_save (editor_page
);
6387 selection_start_marker
= webkit_dom_document_get_element_by_id (
6388 document
, "-x-evo-selection-start-marker");
6389 selection_end_marker
= webkit_dom_document_get_element_by_id (
6390 document
, "-x-evo-selection-end-marker");
6391 current_block
= e_editor_dom_get_parent_block_node_from_child (
6392 WEBKIT_DOM_NODE (selection_start_marker
));
6393 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block
))
6394 current_block
= NULL
;
6396 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
6397 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
6400 ev
= g_new0 (EEditorHistoryEvent
, 1);
6401 ev
->type
= HISTORY_PASTE
;
6403 ev->type = HISTORY_PASTE_AS_TEXT;*/
6405 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
6406 e_editor_dom_selection_get_coordinates (editor_page
,
6407 &ev
->before
.start
.x
,
6408 &ev
->before
.start
.y
,
6413 ev
->before
.end
.x
= ev
->before
.start
.x
;
6414 ev
->before
.end
.y
= ev
->before
.start
.y
;
6417 ev
->data
.string
.from
= NULL
;
6418 ev
->data
.string
.to
= g_strdup (html
);
6421 element
= webkit_dom_document_create_element (document
, "div", NULL
);
6425 webkit_dom_element_set_inner_html (element
, html
, NULL
);
6427 webkit_dom_element_set_attribute (
6428 WEBKIT_DOM_ELEMENT (element
),
6429 "data-evo-html-to-plain-text-wrapper",
6433 /* Add the missing BR elements on the end of DIV and P elements to
6434 * preserve the line breaks. But we need to do that just in case that
6435 * there is another element that contains text. */
6436 preserve_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (element
), "p, div, address");
6437 preserve_line_breaks_in_element (
6439 WEBKIT_DOM_ELEMENT (element
),
6440 "[data-evo-html-to-plain-text-wrapper] > :matches(h1, h2, h3, h4, h5, h6)");
6441 preserve_pre_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (element
));
6443 inner_text
= webkit_dom_html_element_get_inner_text (
6444 WEBKIT_DOM_HTML_ELEMENT (element
));
6445 webkit_dom_html_element_set_inner_text (
6446 WEBKIT_DOM_HTML_ELEMENT (element
), inner_text
, NULL
);
6448 g_free (inner_text
);
6450 webkit_dom_html_element_set_inner_text (
6451 WEBKIT_DOM_HTML_ELEMENT (element
), html
, NULL
);
6453 inner_html
= webkit_dom_element_get_inner_html (element
);
6454 parse_html_into_blocks (editor_page
, element
, WEBKIT_DOM_ELEMENT (current_block
), inner_html
);
6456 g_free (inner_html
);
6458 has_selection
= !e_editor_dom_selection_is_collapsed (editor_page
);
6459 if (has_selection
&& !e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
6460 WebKitDOMRange
*range
= NULL
;
6462 range
= e_editor_dom_get_current_range (editor_page
);
6463 insert_delete_event (editor_page
, range
);
6464 g_clear_object (&range
);
6466 /* Remove the text that was meant to be replaced by the pasted text */
6467 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
6469 e_editor_dom_selection_save (editor_page
);
6471 selection_start_marker
= webkit_dom_document_get_element_by_id (
6472 document
, "-x-evo-selection-start-marker");
6473 selection_end_marker
= webkit_dom_document_get_element_by_id (
6474 document
, "-x-evo-selection-end-marker");
6475 current_block
= e_editor_dom_get_parent_block_node_from_child (
6476 WEBKIT_DOM_NODE (selection_start_marker
));
6477 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block
))
6478 current_block
= NULL
;
6481 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_end_marker
));
6482 /* Pasting into the citation */
6483 if (citation_level
> 0) {
6485 gint word_wrap_length
;
6486 WebKitDOMElement
*br
;
6487 WebKitDOMNode
*first_paragraph
, *last_paragraph
;
6488 WebKitDOMNode
*child
, *parent
, *current_block
;
6490 first_paragraph
= webkit_dom_node_get_first_child (
6491 WEBKIT_DOM_NODE (element
));
6492 last_paragraph
= webkit_dom_node_get_last_child (
6493 WEBKIT_DOM_NODE (element
));
6495 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
6496 length
= word_wrap_length
- 2 * citation_level
;
6498 /* Pasting text that was parsed just into one paragraph */
6499 if (webkit_dom_node_is_same_node (first_paragraph
, last_paragraph
)) {
6500 WebKitDOMNode
*child
, *parent
, *parent_block
;
6502 parent_block
= e_editor_dom_get_parent_block_node_from_child (
6503 WEBKIT_DOM_NODE (selection_start_marker
));
6505 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent_block
));
6506 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent_block
));
6508 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
6509 while ((child
= webkit_dom_node_get_first_child (first_paragraph
))) {
6510 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
) &&
6511 WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child
)) {
6512 WebKitDOMNode
*anchor_child
;
6514 while ((anchor_child
= webkit_dom_node_get_first_child (child
)))
6515 webkit_dom_node_insert_before (
6516 webkit_dom_node_get_parent_node (
6517 WEBKIT_DOM_NODE (selection_start_marker
)),
6519 WEBKIT_DOM_NODE (selection_start_marker
),
6521 remove_node (child
);
6523 webkit_dom_node_insert_before (
6524 webkit_dom_node_get_parent_node (
6525 WEBKIT_DOM_NODE (selection_start_marker
)),
6527 WEBKIT_DOM_NODE (selection_start_marker
),
6531 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
)) {
6532 gchar
*text_content
;
6534 text_content
= webkit_dom_node_get_text_content (parent
);
6536 webkit_dom_element_set_attribute (
6537 WEBKIT_DOM_ELEMENT (parent
),
6541 g_free (text_content
);
6544 parent_block
= WEBKIT_DOM_NODE (
6545 e_editor_dom_wrap_paragraph_length (editor_page
, WEBKIT_DOM_ELEMENT (parent_block
), length
));
6546 webkit_dom_node_normalize (parent_block
);
6547 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (parent_block
), citation_level
);
6549 e_editor_dom_selection_restore (editor_page
);
6554 /* Pasting content parsed into the multiple paragraphs */
6555 parent
= e_editor_dom_get_parent_block_node_from_child (
6556 WEBKIT_DOM_NODE (selection_start_marker
));
6558 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent
));
6559 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent
));
6561 /* Move the elements from the first paragraph before the selection start element */
6562 while ((child
= webkit_dom_node_get_first_child (first_paragraph
)))
6563 webkit_dom_node_insert_before (
6566 WEBKIT_DOM_NODE (selection_start_marker
),
6569 remove_node (first_paragraph
);
6571 /* If the BR element is on the last position, remove it as we don't need it */
6572 child
= webkit_dom_node_get_last_child (parent
);
6573 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
6574 remove_node (child
);
6576 parent
= e_editor_dom_get_parent_block_node_from_child (
6577 WEBKIT_DOM_NODE (selection_end_marker
));
6579 child
= webkit_dom_node_get_next_sibling (
6580 WEBKIT_DOM_NODE (selection_end_marker
));
6581 /* Move the elements that are in the same paragraph as the selection end
6582 * on the end of pasted text, but avoid BR on the end of paragraph */
6584 WebKitDOMNode
*next_child
=
6585 webkit_dom_node_get_next_sibling (child
);
6586 if (!(!next_child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
)))
6587 webkit_dom_node_append_child (last_paragraph
, child
, NULL
);
6591 current_block
= e_editor_dom_get_parent_block_node_from_child (
6592 WEBKIT_DOM_NODE (selection_start_marker
));
6594 dom_remove_selection_markers (document
);
6596 /* Caret will be restored on the end of pasted text */
6597 webkit_dom_node_append_child (
6599 WEBKIT_DOM_NODE (dom_create_selection_marker (document
, TRUE
)),
6602 webkit_dom_node_append_child (
6604 WEBKIT_DOM_NODE (dom_create_selection_marker (document
, FALSE
)),
6607 /* Insert the paragraph with the end of the pasted text after
6608 * the paragraph that contains the selection end */
6609 webkit_dom_node_insert_before (
6610 webkit_dom_node_get_parent_node (parent
),
6612 webkit_dom_node_get_next_sibling (parent
),
6615 /* Wrap, quote and move all paragraphs from pasted text into the body */
6616 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
)))) {
6617 child
= WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (
6618 editor_page
, WEBKIT_DOM_ELEMENT (child
), length
));
6619 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (child
), citation_level
);
6620 webkit_dom_node_insert_before (
6621 webkit_dom_node_get_parent_node (last_paragraph
),
6627 webkit_dom_node_normalize (last_paragraph
);
6629 last_paragraph
= WEBKIT_DOM_NODE (
6630 e_editor_dom_wrap_paragraph_length (
6631 editor_page
, WEBKIT_DOM_ELEMENT (last_paragraph
), length
));
6632 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (last_paragraph
), citation_level
);
6634 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent
));
6635 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent
));
6637 current_block
= WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (
6638 editor_page
, WEBKIT_DOM_ELEMENT (current_block
), length
));
6639 e_editor_dom_quote_plain_text_element_after_wrapping (editor_page
, WEBKIT_DOM_ELEMENT (current_block
), citation_level
);
6641 if ((br
= webkit_dom_document_get_element_by_id (document
, "-x-evo-first-br")))
6642 webkit_dom_element_remove_attribute (br
, "class");
6644 if ((br
= webkit_dom_document_get_element_by_id (document
, "-x-evo-last-br")))
6645 webkit_dom_element_remove_attribute (br
, "class");
6648 e_editor_dom_selection_get_coordinates (editor_page
,
6653 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
6656 e_editor_dom_selection_restore (editor_page
);
6661 remove_node (WEBKIT_DOM_NODE (selection_start_marker
));
6662 remove_node (WEBKIT_DOM_NODE (selection_end_marker
));
6664 /* If the text to insert was converted just to one block, pass just its
6665 * text to WebKit otherwise WebKit will insert unwanted block with
6666 * extra new line. */
6667 if (!webkit_dom_node_get_next_sibling (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
))))
6668 inner_html
= webkit_dom_element_get_inner_html (
6669 WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
))));
6671 inner_html
= webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (element
));
6673 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_HTML
, inner_html
);
6675 if (g_str_has_suffix (inner_html
, " "))
6676 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT
, " ");
6678 g_free (inner_html
);
6680 e_editor_dom_selection_save (editor_page
);
6682 element
= webkit_dom_document_query_selector (
6683 document
, "* > br#-x-evo-first-br", NULL
);
6685 WebKitDOMNode
*sibling
;
6686 WebKitDOMNode
*parent
;
6688 parent
= webkit_dom_node_get_parent_node (
6689 WEBKIT_DOM_NODE (element
));
6691 sibling
= webkit_dom_node_get_previous_sibling (parent
);
6693 remove_node (WEBKIT_DOM_NODE (parent
));
6695 webkit_dom_element_remove_attribute (element
, "id");
6698 element
= webkit_dom_document_query_selector (
6699 document
, "* > br#-x-evo-last-br", NULL
);
6701 WebKitDOMNode
*parent
;
6702 WebKitDOMNode
*child
;
6704 parent
= webkit_dom_node_get_parent_node (
6705 WEBKIT_DOM_NODE (element
));
6707 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent
));
6709 node
= webkit_dom_node_get_first_child (node
);
6711 inner_html
= webkit_dom_node_get_text_content (node
);
6712 if (g_str_has_prefix (inner_html
, UNICODE_NBSP
))
6713 webkit_dom_character_data_replace_data (
6714 WEBKIT_DOM_CHARACTER_DATA (node
), 0, 1, "", NULL
);
6715 g_free (inner_html
);
6719 selection_end_marker
= webkit_dom_document_get_element_by_id (
6720 document
, "-x-evo-selection-end-marker");
6722 if (has_selection
) {
6723 /* Everything after the selection end marker have to be in separate
6725 child
= webkit_dom_node_get_next_sibling (
6726 WEBKIT_DOM_NODE (selection_end_marker
));
6727 /* Move the elements that are in the same paragraph as the selection end
6728 * on the end of pasted text, but avoid BR on the end of paragraph */
6730 WebKitDOMNode
*next_child
=
6731 webkit_dom_node_get_next_sibling (child
);
6732 if (!(!next_child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
)))
6733 webkit_dom_node_append_child (parent
, child
, NULL
);
6737 remove_node (WEBKIT_DOM_NODE (element
));
6739 webkit_dom_node_insert_before (
6740 webkit_dom_node_get_parent_node (
6741 webkit_dom_node_get_parent_node (
6742 WEBKIT_DOM_NODE (selection_end_marker
))),
6744 webkit_dom_node_get_next_sibling (
6745 webkit_dom_node_get_parent_node (
6746 WEBKIT_DOM_NODE (selection_end_marker
))),
6750 node
= webkit_dom_node_get_next_sibling (parent
);
6752 fix_structure_after_pasting_multiline_content (parent
);
6753 if (!webkit_dom_node_get_first_child (parent
))
6754 remove_node (parent
);
6759 /* Restore caret on the end of pasted text */
6760 webkit_dom_node_insert_before (
6762 WEBKIT_DOM_NODE (selection_end_marker
),
6763 webkit_dom_node_get_first_child (node
),
6766 selection_start_marker
= webkit_dom_document_get_element_by_id (
6767 document
, "-x-evo-selection-start-marker");
6768 webkit_dom_node_insert_before (
6770 WEBKIT_DOM_NODE (selection_start_marker
),
6771 webkit_dom_node_get_first_child (node
),
6776 webkit_dom_element_remove_attribute (element
, "id");
6778 if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent
)) && !has_selection
)
6779 remove_node (parent
);
6781 /* When pasting the content that was copied from the composer, WebKit
6782 * restores the selection wrongly, thus is saved wrongly and we have
6784 WebKitDOMNode
*block
, *parent
, *clone1
, *clone2
;
6786 selection_start_marker
= webkit_dom_document_get_element_by_id (
6787 document
, "-x-evo-selection-start-marker");
6788 selection_end_marker
= webkit_dom_document_get_element_by_id (
6789 document
, "-x-evo-selection-end-marker");
6791 block
= e_editor_dom_get_parent_block_node_from_child (
6792 WEBKIT_DOM_NODE (selection_start_marker
));
6793 parent
= webkit_dom_node_get_parent_node (block
);
6794 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (parent
), "id");
6796 /* Check if WebKit created wrong structure */
6797 clone1
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block
), FALSE
, NULL
);
6798 clone2
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (parent
), FALSE
, NULL
);
6799 if (webkit_dom_node_is_equal_node (clone1
, clone2
) ||
6800 (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone1
) && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone2
) &&
6801 !element_has_class (WEBKIT_DOM_ELEMENT (clone2
), "-x-evo-indented"))) {
6802 fix_structure_after_pasting_multiline_content (block
);
6803 if (g_strcmp0 (html
, "\n") == 0) {
6804 WebKitDOMElement
*br
;
6806 br
= webkit_dom_document_create_element (document
, "br", NULL
);
6807 webkit_dom_node_append_child (
6808 parent
, WEBKIT_DOM_NODE (br
), NULL
);
6810 webkit_dom_node_insert_before (
6812 WEBKIT_DOM_NODE (selection_start_marker
),
6813 webkit_dom_node_get_last_child (parent
),
6815 } else if (!webkit_dom_node_get_first_child (parent
))
6816 remove_node (parent
);
6819 webkit_dom_node_insert_before (
6820 webkit_dom_node_get_parent_node (
6821 WEBKIT_DOM_NODE (selection_start_marker
)),
6822 WEBKIT_DOM_NODE (selection_end_marker
),
6823 webkit_dom_node_get_next_sibling (
6824 WEBKIT_DOM_NODE (selection_start_marker
)),
6829 e_editor_dom_selection_get_coordinates (editor_page
,
6834 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
6837 e_editor_dom_selection_restore (editor_page
);
6839 e_editor_dom_force_spell_check_in_viewport (editor_page
);
6840 e_editor_dom_scroll_to_caret (editor_page
);
6842 e_editor_dom_register_input_event_listener_on_body (editor_page
);
6844 e_editor_page_emit_content_changed (editor_page
);
6848 get_indentation_level (WebKitDOMElement
*element
)
6850 WebKitDOMElement
*parent
;
6856 if (element_has_class (element
, "-x-evo-indented"))
6859 parent
= webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element
));
6860 /* Count level of indentation */
6861 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
6862 if (element_has_class (parent
, "-x-evo-indented"))
6865 parent
= webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent
));
6872 process_indented_element (WebKitDOMElement
*element
)
6875 WebKitDOMNode
*child
;
6876 gboolean needs_indent
= TRUE
;
6881 spaces
= g_strnfill (SPACES_PER_INDENTATION
* get_indentation_level (element
), ' ');
6883 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
));
6885 /* If next sibling is indented blockqoute skip it,
6886 * it will be processed afterwards */
6887 if (WEBKIT_DOM_IS_ELEMENT (child
) &&
6888 element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-indented")) {
6889 child
= webkit_dom_node_get_next_sibling (child
);
6894 if (WEBKIT_DOM_IS_TEXT (child
)) {
6895 gchar
*text_content
;
6896 gchar
*indented_text
= NULL
;
6898 text_content
= webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (child
));
6900 indented_text
= g_strconcat (spaces
, text_content
, NULL
);
6901 needs_indent
= FALSE
;
6904 webkit_dom_character_data_set_data (
6905 WEBKIT_DOM_CHARACTER_DATA (child
),
6906 indented_text
? indented_text
: text_content
,
6909 g_free (text_content
);
6910 g_free (indented_text
);
6911 } else if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
) ||
6912 WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child
) ||
6913 WEBKIT_DOM_IS_HTML_PRE_ELEMENT (child
)) {
6914 needs_indent
= TRUE
;
6917 /* Move to next node */
6918 if (webkit_dom_node_has_child_nodes (child
))
6919 child
= webkit_dom_node_get_first_child (child
);
6920 else if (webkit_dom_node_get_next_sibling (child
))
6921 child
= webkit_dom_node_get_next_sibling (child
);
6923 if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (element
), child
))
6926 child
= webkit_dom_node_get_parent_node (child
);
6928 child
= webkit_dom_node_get_next_sibling (child
);
6933 webkit_dom_element_remove_attribute (element
, "style");
6937 process_quote_nodes (WebKitDOMElement
*blockquote
)
6939 WebKitDOMHTMLCollection
*collection
= NULL
;
6942 /* Replace quote nodes with symbols */
6943 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
6944 blockquote
, "-x-evo-quoted");
6945 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
6946 WebKitDOMNode
*quoted_node
;
6947 gchar
*text_content
;
6949 quoted_node
= webkit_dom_html_collection_item (collection
, ii
);
6950 text_content
= webkit_dom_node_get_text_content (quoted_node
);
6951 webkit_dom_element_set_outer_html (
6952 WEBKIT_DOM_ELEMENT (quoted_node
), text_content
, NULL
);
6953 g_free (text_content
);
6955 g_clear_object (&collection
);
6958 /* Taken from GtkHTML */
6960 get_alpha_value (gint value
,
6964 gint add
= lower
? 'a' : 'A';
6966 str
= g_string_new (". ");
6969 g_string_prepend_c (str
, ((value
- 1) % 26) + add
);
6970 value
= (value
- 1) / 26;
6973 return g_string_free (str
, FALSE
);
6976 /* Taken from GtkHTML */
6978 get_roman_value (gint value
,
6982 const gchar
*base
= "IVXLCDM";
6983 gint b
, r
, add
= lower
? 'a' - 'A' : 0;
6986 return g_strdup ("?. ");
6988 str
= g_string_new (". ");
6990 for (b
= 0; value
> 0 && b
< 7 - 1; b
+= 2, value
/= 10) {
6995 g_string_prepend_c (str
, base
[b
] + add
);
6996 } else if (r
== 4) {
6997 g_string_prepend_c (str
, base
[b
+ 1] + add
);
6998 g_string_prepend_c (str
, base
[b
] + add
);
6999 } else if (r
== 5) {
7000 g_string_prepend_c (str
, base
[b
+ 1] + add
);
7003 g_string_prepend_c (str
, base
[b
] + add
);
7004 g_string_prepend_c (str
, base
[b
+ 1] + add
);
7005 } else if (r
== 9) {
7006 g_string_prepend_c (str
, base
[b
+ 2] + add
);
7007 g_string_prepend_c (str
, base
[b
] + add
);
7012 return g_string_free (str
, FALSE
);
7016 process_list_to_plain_text (EEditorPage
*editor_page
,
7017 WebKitDOMElement
*element
,
7021 EContentEditorBlockFormat format
;
7022 EContentEditorAlignment alignment
;
7024 gboolean empty
= TRUE
;
7025 gchar
*indent_per_level
;
7026 WebKitDOMNode
*item
;
7027 gint word_wrap_length
;
7029 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
7031 indent_per_level
= g_strnfill (SPACES_PER_LIST_LEVEL
, ' ');
7032 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
7033 format
= dom_get_list_format_from_node (
7034 WEBKIT_DOM_NODE (element
));
7036 /* Process list items to plain text */
7037 item
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
));
7039 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (item
))
7040 g_string_append (output
, "\n");
7042 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
7043 gchar
*space
= NULL
, *item_str
= NULL
;
7045 WebKitDOMElement
*wrapped
;
7046 GString
*item_value
= g_string_new ("");
7050 alignment
= e_editor_dom_get_list_alignment_from_node (
7051 WEBKIT_DOM_NODE (item
));
7053 wrapped
= webkit_dom_element_query_selector (
7054 WEBKIT_DOM_ELEMENT (item
), ".-x-evo-wrap-br", NULL
);
7057 WebKitDOMNode
*node
= webkit_dom_node_get_first_child (item
);
7058 GString
*line
= g_string_new ("");
7061 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
) &&
7062 element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-wrap-br")) {
7063 g_string_append (line
, "\n");
7064 /* put spaces before line characters -> wordwraplength - indentation */
7065 for (ii
= 0; ii
< level
; ii
++)
7066 g_string_append (line
, indent_per_level
);
7067 if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element
))
7068 g_string_append (line
, indent_per_level
);
7069 g_string_append (item_value
, line
->str
);
7070 g_string_erase (line
, 0, -1);
7072 /* append text from node to line */
7073 gchar
*text_content
;
7074 text_content
= webkit_dom_node_get_text_content (node
);
7075 g_string_append (line
, text_content
);
7076 g_free (text_content
);
7078 node
= webkit_dom_node_get_next_sibling (node
);
7081 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_LEFT
)
7082 g_string_append (item_value
, line
->str
);
7084 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_CENTER
) {
7088 fill_length
= word_wrap_length
- g_utf8_strlen (line
->str
, -1);
7089 fill_length
-= ii
* SPACES_PER_LIST_LEVEL
;
7090 if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element
))
7091 fill_length
+= SPACES_PER_LIST_LEVEL
;
7094 if (fill_length
< 0)
7097 fill
= g_strnfill (fill_length
, ' ');
7099 g_string_append (item_value
, fill
);
7100 g_string_append (item_value
, line
->str
);
7104 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_RIGHT
) {
7108 fill_length
= word_wrap_length
- g_utf8_strlen (line
->str
, -1);
7109 fill_length
-= ii
* SPACES_PER_LIST_LEVEL
;
7111 if (fill_length
< 0)
7114 fill
= g_strnfill (fill_length
, ' ');
7116 g_string_append (item_value
, fill
);
7117 g_string_append (item_value
, line
->str
);
7120 g_string_free (line
, TRUE
);
7121 /* that same here */
7123 gchar
*text_content
=
7124 webkit_dom_node_get_text_content (item
);
7126 g_string_append (item_value
, text_content
);
7127 g_free (text_content
);
7130 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
) {
7131 space
= g_strnfill (SPACES_PER_LIST_LEVEL
- 2, ' ');
7132 item_str
= g_strdup_printf (
7133 "%s* %s", space
, item_value
->str
);
7137 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
) {
7138 gint length
= 1, tmp
= counter
, spaces_count
;
7140 while ((tmp
= tmp
/ 10) > 1)
7146 spaces_count
= SPACES_ORDERED_LIST_FIRST_LEVEL
- 2 - length
;
7147 if (spaces_count
> 0)
7148 space
= g_strnfill (spaces_count
, ' ');
7150 item_str
= g_strdup_printf (
7151 "%s%d. %s", space
&& *space
? space
: "", counter
, item_value
->str
);
7155 if (format
> E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
) {
7156 gchar
*value
, spaces_count
;
7158 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
)
7159 value
= get_alpha_value (counter
, FALSE
);
7161 value
= get_roman_value (counter
, FALSE
);
7163 spaces_count
= SPACES_ORDERED_LIST_FIRST_LEVEL
- strlen (value
);
7164 if (spaces_count
> 0)
7165 space
= g_strnfill (spaces_count
, ' ');
7166 item_str
= g_strdup_printf (
7167 "%s%s%s", space
&& *space
? space
: "" , value
, item_value
->str
);
7172 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_LEFT
) {
7173 for (ii
= 0; ii
< level
- 1; ii
++) {
7174 g_string_append (output
, indent_per_level
);
7176 if (WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (element
))
7177 if (dom_node_find_parent_element (item
, "OL"))
7178 g_string_append (output
, indent_per_level
);
7179 g_string_append (output
, item_str
);
7182 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_RIGHT
) {
7187 fill_length
= word_wrap_length
- g_utf8_strlen (item_str
, -1);
7188 fill_length
-= ii
* SPACES_PER_LIST_LEVEL
;
7190 if (fill_length
< 0)
7193 if (g_str_has_suffix (item_str
, " "))
7196 fill
= g_strnfill (fill_length
, ' ');
7198 g_string_append (output
, fill
);
7201 if (g_str_has_suffix (item_str
, " "))
7202 g_string_append_len (output
, item_str
, g_utf8_strlen (item_str
, -1) - 1);
7204 g_string_append (output
, item_str
);
7207 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_CENTER
) {
7210 gint fill_length
= 0;
7212 for (ii
= 0; ii
< level
- 1; ii
++)
7213 g_string_append (output
, indent_per_level
);
7215 fill_length
= word_wrap_length
- g_utf8_strlen (item_str
, -1);
7216 fill_length
-= ii
* SPACES_PER_LIST_LEVEL
;
7217 if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element
))
7218 fill_length
+= SPACES_PER_LIST_LEVEL
;
7221 if (fill_length
< 0)
7224 if (g_str_has_suffix (item_str
, " "))
7227 fill
= g_strnfill (fill_length
, ' ');
7229 g_string_append (output
, fill
);
7232 if (g_str_has_suffix (item_str
, " "))
7233 g_string_append_len (output
, item_str
, g_utf8_strlen (item_str
, -1) - 1);
7235 g_string_append (output
, item_str
);
7239 item
= webkit_dom_node_get_next_sibling (item
);
7241 g_string_append (output
, "\n");
7244 g_string_free (item_value
, TRUE
);
7245 } else if (node_is_list (item
)) {
7246 process_list_to_plain_text (
7247 editor_page
, WEBKIT_DOM_ELEMENT (item
), level
+ 1, output
);
7248 item
= webkit_dom_node_get_next_sibling (item
);
7250 item
= webkit_dom_node_get_next_sibling (item
);
7254 if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
)) && !empty
)
7255 g_string_append (output
, "\n");
7257 g_free (indent_per_level
);
7261 remove_base_attributes (WebKitDOMElement
*element
)
7263 webkit_dom_element_remove_attribute (element
, "class");
7264 webkit_dom_element_remove_attribute (element
, "id");
7265 webkit_dom_element_remove_attribute (element
, "name");
7269 remove_evolution_attributes (WebKitDOMElement
*element
)
7271 webkit_dom_element_remove_attribute (element
, "data-evo-paragraph");
7272 webkit_dom_element_remove_attribute (element
, "data-converted");
7273 webkit_dom_element_remove_attribute (element
, "data-edit-as-new");
7274 webkit_dom_element_remove_attribute (element
, "data-evo-draft");
7275 webkit_dom_element_remove_attribute (element
, "data-inline");
7276 webkit_dom_element_remove_attribute (element
, "data-uri");
7277 webkit_dom_element_remove_attribute (element
, "data-message");
7278 webkit_dom_element_remove_attribute (element
, "data-name");
7279 webkit_dom_element_remove_attribute (element
, "data-new-message");
7280 webkit_dom_element_remove_attribute (element
, "data-user-wrapped");
7281 webkit_dom_element_remove_attribute (element
, "data-evo-plain-text");
7282 webkit_dom_element_remove_attribute (element
, "data-plain-text-style");
7283 webkit_dom_element_remove_attribute (element
, "data-style");
7284 webkit_dom_element_remove_attribute (element
, "spellcheck");
7288 convert_element_from_html_to_plain_text (EEditorPage
*editor_page
,
7289 WebKitDOMElement
*element
,
7293 WebKitDOMDocument
*document
;
7294 WebKitDOMElement
*top_signature
, *signature
, *blockquote
, *main_blockquote
, *br_element
;
7295 WebKitDOMNode
*signature_clone
, *from
;
7296 gint blockquotes_count
;
7297 gchar
*inner_text
, *inner_html
;
7299 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
7301 document
= e_editor_page_get_document (editor_page
);
7302 top_signature
= webkit_dom_element_query_selector (
7303 element
, ".-x-evo-top-signature", NULL
);
7304 signature
= webkit_dom_element_query_selector (
7305 element
, "span.-x-evo-signature", NULL
);
7306 main_blockquote
= webkit_dom_element_query_selector (
7307 element
, "#-x-evo-main-cite", NULL
);
7309 blockquote
= webkit_dom_document_create_element (
7310 document
, "blockquote", NULL
);
7312 if (main_blockquote
) {
7313 webkit_dom_element_set_attribute (
7314 blockquote
, "type", "cite", NULL
);
7315 from
= WEBKIT_DOM_NODE (main_blockquote
);
7318 WebKitDOMNode
*parent
= webkit_dom_node_get_parent_node (
7319 WEBKIT_DOM_NODE (signature
));
7320 signature_clone
= webkit_dom_node_clone_node_with_error (parent
, TRUE
, NULL
);
7321 remove_node (parent
);
7323 from
= WEBKIT_DOM_NODE (element
);
7326 blockquotes_count
= create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (from
));
7327 create_text_markers_for_selection_in_element (WEBKIT_DOM_ELEMENT (from
));
7328 webkit_dom_element_set_attribute (
7329 WEBKIT_DOM_ELEMENT (from
),
7330 "data-evo-html-to-plain-text-wrapper",
7334 /* Add the missing BR elements on the end of DIV and P elements to
7335 * preserve the line breaks. But we need to do that just in case that
7336 * there is another element that contains text. */
7337 preserve_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (from
), "p, div, address");
7338 preserve_line_breaks_in_element (
7340 WEBKIT_DOM_ELEMENT (from
),
7341 "[data-evo-html-to-plain-text-wrapper] > :matches(h1, h2, h3, h4, h5, h6)");
7342 preserve_pre_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (element
));
7344 webkit_dom_element_remove_attribute (
7345 WEBKIT_DOM_ELEMENT (from
), "data-evo-html-to-plain-text-wrapper");
7347 inner_text
= webkit_dom_html_element_get_inner_text (
7348 WEBKIT_DOM_HTML_ELEMENT (from
));
7350 webkit_dom_html_element_set_inner_text (
7351 WEBKIT_DOM_HTML_ELEMENT (blockquote
), inner_text
, NULL
);
7353 inner_html
= webkit_dom_element_get_inner_html (blockquote
);
7355 parse_html_into_blocks (editor_page
,
7356 main_blockquote
? blockquote
: WEBKIT_DOM_ELEMENT (element
),
7360 if (main_blockquote
) {
7361 webkit_dom_node_replace_child (
7362 webkit_dom_node_get_parent_node (
7363 WEBKIT_DOM_NODE (main_blockquote
)),
7364 WEBKIT_DOM_NODE (blockquote
),
7365 WEBKIT_DOM_NODE (main_blockquote
),
7368 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element
));
7370 WebKitDOMNode
*first_child
;
7373 if (!top_signature
) {
7374 signature_clone
= webkit_dom_node_append_child (
7375 WEBKIT_DOM_NODE (element
),
7379 webkit_dom_node_insert_before (
7380 WEBKIT_DOM_NODE (element
),
7382 webkit_dom_node_get_first_child (
7383 WEBKIT_DOM_NODE (element
)),
7388 first_child
= webkit_dom_node_get_first_child (
7389 WEBKIT_DOM_NODE (element
));
7391 if (!webkit_dom_node_has_child_nodes (first_child
)) {
7392 webkit_dom_element_set_inner_html (
7393 WEBKIT_DOM_ELEMENT (first_child
),
7397 dom_add_selection_markers_into_element_start (
7398 document
, WEBKIT_DOM_ELEMENT (first_child
), NULL
, NULL
);
7405 *quote
= main_blockquote
|| blockquotes_count
> 0;
7407 webkit_dom_element_set_attribute (
7408 WEBKIT_DOM_ELEMENT (element
), "data-converted", "", NULL
);
7410 if ((br_element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-first-br")))
7411 webkit_dom_element_remove_attribute (br_element
, "id");
7413 if ((br_element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-last-br")))
7414 webkit_dom_element_remove_attribute (br_element
, "id");
7416 g_free (inner_text
);
7417 g_free (inner_html
);
7421 e_editor_dom_convert_element_from_html_to_plain_text (EEditorPage
*editor_page
,
7422 WebKitDOMElement
*element
)
7424 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
7426 convert_element_from_html_to_plain_text (editor_page
, element
, NULL
, NULL
);
7430 process_node_to_plain_text_changing_composer_mode (EEditorPage
*editor_page
,
7431 WebKitDOMNode
*source
)
7433 WebKitDOMElement
*element
;
7434 WebKitDOMNamedNodeMap
*attributes
= NULL
;
7437 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
7439 attributes
= webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (source
));
7440 for (ii
= webkit_dom_named_node_map_get_length (attributes
); ii
--;) {
7442 WebKitDOMAttr
*attribute
;
7444 attribute
= WEBKIT_DOM_ATTR (webkit_dom_named_node_map_item (attributes
, ii
));
7446 name
= webkit_dom_attr_get_name (attribute
);
7448 if (g_strcmp0 (name
, "bgcolor") == 0 ||
7449 g_strcmp0 (name
, "text") == 0 ||
7450 g_strcmp0 (name
, "vlink") == 0 ||
7451 g_strcmp0 (name
, "link") == 0) {
7453 webkit_dom_element_remove_attribute_node (
7454 WEBKIT_DOM_ELEMENT (source
), attribute
, NULL
);
7458 g_clear_object (&attributes
);
7461 element
= webkit_dom_element_query_selector (
7462 WEBKIT_DOM_ELEMENT (source
), "div.-x-evo-signature-wrapper", NULL
);
7464 WebKitDOMNode
*first_child
;
7467 first_child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
));
7468 id
= webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child
));
7470 if (g_strcmp0 (id
, "none") != 0)
7471 convert_element_from_html_to_plain_text (
7472 editor_page
, WEBKIT_DOM_ELEMENT (first_child
), NULL
, NULL
);
7477 /* This function is different than the others there as this needs to go through
7478 * the DOM node by node and generate the plain text of their content. For some
7479 * it will just take the text content, but for example the lists are not that
7482 process_node_to_plain_text_for_exporting (EEditorPage
*editor_page
,
7483 WebKitDOMNode
*source
,
7486 WebKitDOMNodeList
*nodes
= NULL
;
7488 gchar
*content
= NULL
;
7491 html_mode
= e_editor_page_get_html_mode (editor_page
);
7493 nodes
= webkit_dom_node_get_child_nodes (source
);
7494 length
= webkit_dom_node_list_get_length (nodes
);
7495 for (ii
= 0; ii
< length
; ii
++) {
7496 WebKitDOMNode
*child
;
7497 gboolean skip_node
= FALSE
;
7499 child
= webkit_dom_node_list_item (nodes
, ii
);
7501 if (WEBKIT_DOM_IS_TEXT (child
)) {
7503 const gchar
*css_align
= NULL
;
7506 content
= webkit_dom_node_get_text_content (child
);
7510 /* The text nodes with only '\n' are reflected only in
7511 * PRE elements, otherwise skip them. */
7512 /* FIXME wrong for "white-space: pre", but we don't use
7513 * that in editor in our expected DOM structure */
7514 if (content
[0] == '\n' && content
[1] == '\0' &&
7515 !WEBKIT_DOM_IS_HTML_PRE_ELEMENT (source
)) {
7521 if (strstr (content
, UNICODE_ZERO_WIDTH_SPACE
)) {
7524 regex
= g_regex_new (UNICODE_ZERO_WIDTH_SPACE
, 0, 0, NULL
);
7525 tmp
= g_regex_replace (
7526 regex
, content
, -1, 0, "", 0, NULL
);
7529 g_regex_unref (regex
);
7532 if (strstr (content
, UNICODE_NBSP
)) {
7535 regex
= g_regex_new (UNICODE_NBSP
, 0, 0, NULL
);
7536 tmp
= g_regex_replace (regex
, content
, -1, 0, " ", 0, NULL
);
7539 g_regex_unref (regex
);
7542 class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (source
));
7543 if (class && (css_align
= strstr (class, "-x-evo-align-"))) {
7544 gchar
*content_with_align
;
7545 gint word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
7547 if (!g_str_has_prefix (css_align
+ 13, "left")) {
7551 if (g_str_has_prefix (css_align
+ 13, "center"))
7552 len
= (word_wrap_length
- g_utf8_strlen (content
, -1)) / 2;
7554 len
= word_wrap_length
- g_utf8_strlen (content
, -1);
7559 if (g_str_has_suffix (content
, " ")) {
7563 align
= g_strnfill (len
, ' ');
7565 tmp
= g_strndup (content
, g_utf8_strlen (content
, -1) -1);
7567 content_with_align
= g_strconcat (
7571 align
= g_strnfill (len
, ' ');
7573 content_with_align
= g_strconcat (
7574 align
, content
, NULL
);
7579 content
= content_with_align
;
7585 g_string_append (buffer
, content
);
7593 if (!WEBKIT_DOM_IS_ELEMENT (child
))
7596 if (element_has_class (WEBKIT_DOM_ELEMENT (child
), "Apple-tab-span")) {
7597 content
= webkit_dom_node_get_text_content (child
);
7598 g_string_append (buffer
, content
);
7604 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child
))
7605 process_quote_nodes (WEBKIT_DOM_ELEMENT (child
));
7607 if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child
) &&
7608 element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-indented"))
7609 process_indented_element (WEBKIT_DOM_ELEMENT (child
));
7611 if (node_is_list (child
)) {
7612 process_list_to_plain_text (editor_page
, WEBKIT_DOM_ELEMENT (child
), 1, buffer
);
7617 if (element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-resizable-wrapper") &&
7618 !element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-smiley-wrapper")) {
7624 if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child
) &&
7625 element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-signature-wrapper")) {
7626 WebKitDOMNode
*first_child
;
7629 first_child
= webkit_dom_node_get_first_child (child
);
7631 /* Don't generate any text if the signature is set to None. */
7632 id
= webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child
));
7633 if (g_strcmp0 (id
, "none") == 0) {
7636 remove_node (child
);
7645 convert_element_from_html_to_plain_text (
7646 editor_page
, WEBKIT_DOM_ELEMENT (first_child
), NULL
, NULL
);
7651 /* Replace smileys with their text representation */
7652 if (element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-smiley-wrapper")) {
7653 WebKitDOMNode
*text_version
;
7655 text_version
= webkit_dom_node_get_last_child (child
);
7656 content
= webkit_dom_html_element_get_inner_text (
7657 WEBKIT_DOM_HTML_ELEMENT (text_version
));
7658 g_string_append (buffer
, content
);
7664 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
)) {
7665 g_string_append (buffer
, "\n");
7669 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child
)) {
7670 content
= webkit_dom_html_element_get_inner_text (
7671 WEBKIT_DOM_HTML_ELEMENT (child
));
7672 g_string_append (buffer
, content
);
7677 if (!skip_node
&& webkit_dom_node_has_child_nodes (child
))
7678 process_node_to_plain_text_for_exporting (editor_page
, child
, buffer
);
7680 g_clear_object (&nodes
);
7682 if (!g_str_has_suffix (buffer
->str
, "\n") &&
7683 (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (source
) ||
7684 WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (source
) ||
7685 WEBKIT_DOM_IS_HTML_PRE_ELEMENT (source
) ||
7686 WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (source
)))
7687 g_string_append (buffer
, "\n");
7689 if (g_str_has_suffix (buffer
->str
, "\n") && buffer
->len
> 1 &&
7690 WEBKIT_DOM_IS_HTML_BODY_ELEMENT (source
))
7691 g_string_truncate (buffer
, buffer
->len
- 1);
7695 process_node_to_html_changing_composer_mode (EEditorPage
*editor_page
,
7696 WebKitDOMNode
*source
)
7701 process_node_to_html_for_exporting (EEditorPage
*editor_page
,
7702 WebKitDOMNode
*source
)
7704 WebKitDOMNodeList
*list
= NULL
;
7705 WebKitDOMHTMLCollection
*collection
= NULL
;
7706 WebKitDOMElement
*element
;
7707 WebKitDOMDocument
*document
;
7710 document
= webkit_dom_node_get_owner_document (source
);
7712 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (source
));
7714 /* Aligned elements */
7715 list
= webkit_dom_element_query_selector_all (
7716 WEBKIT_DOM_ELEMENT (source
), "[class*=\"-x-evo-align\"]", NULL
);
7717 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
7718 gchar
*class = NULL
;
7719 WebKitDOMNode
*node
;
7720 gboolean center
= FALSE
;
7722 node
= webkit_dom_node_list_item (list
, ii
);
7723 class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (node
));
7724 center
= g_strrstr (class, "center") != NULL
;
7725 if (center
|| g_strrstr (class, "right")) {
7726 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node
))
7727 webkit_dom_element_set_attribute (
7728 WEBKIT_DOM_ELEMENT (node
),
7731 "list-style-position: inside; text-align: center" :
7732 "list-style-position: inside; text-align: right",
7735 webkit_dom_element_set_attribute (
7736 WEBKIT_DOM_ELEMENT (node
),
7739 "text-align: center" :
7740 "text-align: right",
7743 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-left");
7744 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-center");
7745 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-right");
7748 g_clear_object (&list
);
7750 /* Indented elements */
7751 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
7752 WEBKIT_DOM_ELEMENT (source
), "-x-evo-indented");
7753 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7754 WebKitDOMNode
*node
;
7756 node
= webkit_dom_html_collection_item (collection
, ii
);
7757 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-indented");
7758 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node
));
7760 g_clear_object (&collection
);
7762 /* Tab characters */
7763 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
7764 WEBKIT_DOM_ELEMENT (source
), "Apple-tab-span");
7765 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7766 gchar
*text_content
;
7767 WebKitDOMNode
*node
;
7769 node
= webkit_dom_html_collection_item (collection
, ii
);
7770 text_content
= webkit_dom_node_get_text_content (node
);
7771 webkit_dom_node_insert_before (
7772 webkit_dom_node_get_parent_node (node
),
7773 WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document
, text_content
)),
7778 g_free (text_content
);
7780 g_clear_object (&collection
);
7782 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
7783 WEBKIT_DOM_ELEMENT (source
), "-x-evo-quoted");
7784 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7785 WebKitDOMNode
*quoted_node
;
7786 gchar
*text_content
;
7788 quoted_node
= webkit_dom_html_collection_item (collection
, ii
);
7789 text_content
= webkit_dom_node_get_text_content (quoted_node
);
7790 webkit_dom_element_set_outer_html (
7791 WEBKIT_DOM_ELEMENT (quoted_node
), text_content
, NULL
);
7793 g_free (text_content
);
7795 g_clear_object (&collection
);
7798 list
= webkit_dom_element_query_selector_all (
7799 WEBKIT_DOM_ELEMENT (source
), ".-x-evo-resizable-wrapper:not(.-x-evo-smiley-wrapper)", NULL
);
7800 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
7801 WebKitDOMNode
*node
, *image
;
7803 node
= webkit_dom_node_list_item (list
, ii
);
7804 image
= webkit_dom_node_get_first_child (node
);
7806 if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image
)) {
7807 remove_evolution_attributes (
7808 WEBKIT_DOM_ELEMENT (image
));
7810 webkit_dom_node_replace_child (
7811 webkit_dom_node_get_parent_node (node
), image
, node
, NULL
);
7814 g_clear_object (&list
);
7817 element
= webkit_dom_element_query_selector (
7818 WEBKIT_DOM_ELEMENT (source
), "div.-x-evo-signature-wrapper", NULL
);
7820 WebKitDOMNode
*first_child
;
7823 /* Don't generate any text if the signature is set to None. */
7824 first_child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
));
7825 id
= webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child
));
7826 if (g_strcmp0 (id
, "none") == 0) {
7827 remove_node (WEBKIT_DOM_NODE (element
));
7829 remove_base_attributes (element
);
7830 remove_base_attributes (WEBKIT_DOM_ELEMENT (first_child
));
7831 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (first_child
));
7837 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
7838 WEBKIT_DOM_ELEMENT (source
), "-x-evo-smiley-wrapper");
7839 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7840 WebKitDOMNode
*node
;
7841 WebKitDOMElement
*img
;
7843 node
= webkit_dom_html_collection_item (collection
, ii
);
7844 img
= WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (node
));
7846 remove_evolution_attributes (img
);
7847 remove_base_attributes (img
);
7849 webkit_dom_node_replace_child (
7850 webkit_dom_node_get_parent_node (node
),
7851 WEBKIT_DOM_NODE (img
),
7855 g_clear_object (&collection
);
7857 collection
= webkit_dom_element_get_elements_by_tag_name_as_html_collection (
7858 WEBKIT_DOM_ELEMENT (source
), "pre");
7859 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7860 WebKitDOMNode
*node
;
7862 node
= webkit_dom_html_collection_item (collection
, ii
);
7863 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node
));
7865 g_clear_object (&collection
);
7867 list
= webkit_dom_element_query_selector_all (
7868 WEBKIT_DOM_ELEMENT (source
), "[data-evo-paragraph]", NULL
);
7869 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
7870 WebKitDOMNode
*node
;
7872 node
= webkit_dom_node_list_item (list
, ii
);
7873 remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node
));
7874 remove_base_attributes (WEBKIT_DOM_ELEMENT (node
));
7876 g_clear_object (&list
);
7878 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
7879 WEBKIT_DOM_ELEMENT (source
), "-x-evo-wrap-br");
7880 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7881 WebKitDOMNode
*node
;
7883 node
= webkit_dom_html_collection_item (collection
, ii
);
7884 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "class");
7886 g_clear_object (&collection
);
7888 list
= webkit_dom_element_query_selector_all (
7889 WEBKIT_DOM_ELEMENT (source
), "#-x-evo-main-cite", NULL
);
7890 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
7891 WebKitDOMNode
*node
;
7893 node
= webkit_dom_node_list_item (list
, ii
);
7894 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "id");
7896 g_clear_object (&list
);
7900 remove_image_attributes_from_element (WebKitDOMElement
*element
)
7902 webkit_dom_element_remove_attribute (element
, "background");
7903 webkit_dom_element_remove_attribute (element
, "data-uri");
7904 webkit_dom_element_remove_attribute (element
, "data-inline");
7905 webkit_dom_element_remove_attribute (element
, "data-name");
7909 remove_background_images_in_element (WebKitDOMElement
*element
)
7912 WebKitDOMNodeList
*images
= NULL
;
7914 images
= webkit_dom_element_query_selector_all (
7915 element
, "[background][data-inline]", NULL
);
7916 for (ii
= webkit_dom_node_list_get_length (images
); ii
--;) {
7917 WebKitDOMElement
*image
= WEBKIT_DOM_ELEMENT (
7918 webkit_dom_node_list_item (images
, ii
));
7920 remove_image_attributes_from_element (image
);
7922 g_clear_object (&images
);
7924 remove_image_attributes_from_element (element
);
7928 remove_images_in_element (WebKitDOMElement
*element
)
7931 WebKitDOMNodeList
*images
= NULL
;
7933 images
= webkit_dom_element_query_selector_all (
7934 element
, "img:not(.-x-evo-smiley-img)", NULL
);
7935 for (ii
= webkit_dom_node_list_get_length (images
); ii
--;)
7936 remove_node (webkit_dom_node_list_item (images
, ii
));
7937 g_clear_object (&images
);
7941 remove_images (WebKitDOMDocument
*document
)
7943 remove_images_in_element (
7944 WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document
)));
7948 toggle_smileys (EEditorPage
*editor_page
)
7950 WebKitDOMDocument
*document
;
7951 WebKitDOMHTMLCollection
*collection
= NULL
;
7955 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
7957 document
= e_editor_page_get_document (editor_page
);
7958 html_mode
= e_editor_page_get_html_mode (editor_page
);
7960 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (
7961 document
, "-x-evo-smiley-img");
7962 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
7963 WebKitDOMNode
*img
= webkit_dom_html_collection_item (collection
, ii
);
7964 WebKitDOMElement
*parent
= webkit_dom_node_get_parent_element (img
);
7967 element_add_class (parent
, "-x-evo-resizable-wrapper");
7969 element_remove_class (parent
, "-x-evo-resizable-wrapper");
7971 g_clear_object (&collection
);
7975 toggle_paragraphs_style_in_element (EEditorPage
*editor_page
,
7976 WebKitDOMElement
*element
,
7980 WebKitDOMNodeList
*paragraphs
= NULL
;
7982 paragraphs
= webkit_dom_element_query_selector_all (
7983 element
, ":not(td) > [data-evo-paragraph]", NULL
);
7985 for (ii
= webkit_dom_node_list_get_length (paragraphs
); ii
--;) {
7987 const gchar
*css_align
;
7988 WebKitDOMNode
*node
= webkit_dom_node_list_item (paragraphs
, ii
);
7991 style
= webkit_dom_element_get_attribute (
7992 WEBKIT_DOM_ELEMENT (node
), "style");
7994 if (style
&& (css_align
= strstr (style
, "text-align: "))) {
7995 webkit_dom_element_set_attribute (
7996 WEBKIT_DOM_ELEMENT (node
),
7998 g_str_has_prefix (css_align
+ 12, "center") ?
7999 "text-align: center" :
8000 "text-align: right",
8003 /* In HTML mode the paragraphs don't have width limit */
8004 webkit_dom_element_remove_attribute (
8005 WEBKIT_DOM_ELEMENT (node
), "style");
8009 WebKitDOMNode
*parent
;
8011 parent
= webkit_dom_node_get_parent_node (node
);
8012 /* If the paragraph is inside indented paragraph don't set
8013 * the style as it will be inherited */
8014 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
) && node_is_list (node
)) {
8017 offset
= WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node
) ?
8018 SPACES_PER_LIST_LEVEL
: SPACES_ORDERED_LIST_FIRST_LEVEL
;
8019 /* In plain text mode the paragraphs have width limit */
8020 e_editor_dom_set_paragraph_style (
8021 editor_page
, WEBKIT_DOM_ELEMENT (node
), -1, -offset
, NULL
);
8022 } else if (!element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-indented")) {
8023 const gchar
*style_to_add
= "";
8024 style
= webkit_dom_element_get_attribute (
8025 WEBKIT_DOM_ELEMENT (node
), "style");
8027 if (style
&& (css_align
= strstr (style
, "text-align: "))) {
8028 style_to_add
= g_str_has_prefix (
8029 css_align
+ 12, "center") ?
8030 "text-align: center;" :
8031 "text-align: right;";
8034 /* In plain text mode the paragraphs have width limit */
8035 e_editor_dom_set_paragraph_style (
8036 editor_page
, WEBKIT_DOM_ELEMENT (node
), -1, 0, style_to_add
);
8042 g_clear_object (¶graphs
);
8046 toggle_paragraphs_style (EEditorPage
*editor_page
)
8048 WebKitDOMDocument
*document
;
8050 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8052 document
= e_editor_page_get_document (editor_page
);
8054 toggle_paragraphs_style_in_element (
8056 WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document
)),
8057 e_editor_page_get_html_mode (editor_page
));
8061 e_editor_dom_process_content_for_draft (EEditorPage
*editor_page
,
8062 gboolean only_inner_body
)
8064 WebKitDOMDocument
*document
;
8065 WebKitDOMHTMLElement
*body
;
8066 WebKitDOMElement
*document_element
;
8067 WebKitDOMNodeList
*list
= NULL
;
8068 WebKitDOMNode
*document_element_clone
;
8069 gboolean selection_saved
= FALSE
;
8073 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
8075 document
= e_editor_page_get_document (editor_page
);
8076 body
= webkit_dom_document_get_body (document
);
8078 webkit_dom_element_set_attribute (
8079 WEBKIT_DOM_ELEMENT (body
), "data-evo-draft", "", NULL
);
8081 if (webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker"))
8082 selection_saved
= TRUE
;
8084 if (!selection_saved
)
8085 e_editor_dom_selection_save (editor_page
);
8087 document_element
= webkit_dom_document_get_document_element (document
);
8089 document_element_clone
= webkit_dom_node_clone_node_with_error (
8090 WEBKIT_DOM_NODE (document_element
), TRUE
, NULL
);
8092 list
= webkit_dom_element_query_selector_all (
8093 WEBKIT_DOM_ELEMENT (document_element_clone
), "a.-x-evo-visited-link", NULL
);
8094 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8095 WebKitDOMNode
*anchor
;
8097 anchor
= webkit_dom_node_list_item (list
, ii
);
8098 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (anchor
), "class");
8100 g_clear_object (&list
);
8102 list
= webkit_dom_element_query_selector_all (
8103 WEBKIT_DOM_ELEMENT (document_element_clone
), "#-x-evo-input-start", NULL
);
8104 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8105 WebKitDOMNode
*node
;
8107 node
= webkit_dom_node_list_item (list
, ii
);
8108 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "id");
8110 g_clear_object (&list
);
8112 if (e_editor_page_get_html_mode (editor_page
))
8113 style_blockquotes (WEBKIT_DOM_ELEMENT (document_element_clone
));
8115 if (only_inner_body
) {
8116 WebKitDOMElement
*body
;
8117 WebKitDOMNode
*first_child
;
8119 body
= webkit_dom_element_query_selector (
8120 WEBKIT_DOM_ELEMENT (document_element_clone
), "body", NULL
);
8122 first_child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
8124 if (!e_editor_page_get_html_mode (editor_page
))
8125 webkit_dom_element_set_attribute (
8126 WEBKIT_DOM_ELEMENT (first_child
),
8127 "data-evo-signature-plain-text-mode",
8131 content
= webkit_dom_element_get_inner_html (body
);
8133 if (!e_editor_page_get_html_mode (editor_page
))
8134 webkit_dom_element_remove_attribute (
8135 WEBKIT_DOM_ELEMENT (first_child
),
8136 "data-evo-signature-plain-text-mode");
8138 content
= webkit_dom_element_get_outer_html (
8139 WEBKIT_DOM_ELEMENT (document_element_clone
));
8141 webkit_dom_element_remove_attribute (
8142 WEBKIT_DOM_ELEMENT (body
), "data-evo-draft");
8144 e_editor_dom_selection_restore (editor_page
);
8145 e_editor_dom_force_spell_check_in_viewport (editor_page
);
8147 if (selection_saved
)
8148 e_editor_dom_selection_save (editor_page
);
8154 toggle_indented_elements (EEditorPage
*editor_page
)
8158 WebKitDOMDocument
*document
;
8159 WebKitDOMHTMLCollection
*collection
= NULL
;
8161 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8163 document
= e_editor_page_get_document (editor_page
);
8164 html_mode
= e_editor_page_get_html_mode (editor_page
);
8165 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (
8166 document
, "-x-evo-indented");
8167 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
8168 WebKitDOMNode
*node
= webkit_dom_html_collection_item (collection
, ii
);
8171 dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node
), "style", "data-plain-text-style");
8173 dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node
), "data-plain-text-style", "style");
8175 g_clear_object (&collection
);
8179 process_content_to_html_changing_composer_mode (EEditorPage
*editor_page
)
8181 WebKitDOMDocument
*document
;
8182 WebKitDOMNode
*body
;
8183 WebKitDOMElement
*blockquote
;
8185 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8187 document
= e_editor_page_get_document (editor_page
);
8188 body
= WEBKIT_DOM_NODE (webkit_dom_document_get_body (document
));
8190 webkit_dom_element_remove_attribute (
8191 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text");
8192 blockquote
= webkit_dom_document_query_selector (
8193 document
, "blockquote[type|=cite]", NULL
);
8196 dom_dequote_plain_text (document
);
8198 toggle_paragraphs_style (editor_page
);
8199 toggle_smileys (editor_page
);
8200 remove_images (document
);
8201 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (body
));
8203 process_node_to_html_changing_composer_mode (editor_page
, body
);
8207 wrap_paragraphs_in_quoted_content (EEditorPage
*editor_page
)
8209 WebKitDOMDocument
*document
;
8210 WebKitDOMNodeList
*paragraphs
= NULL
;
8213 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8215 document
= e_editor_page_get_document (editor_page
);
8217 paragraphs
= webkit_dom_document_query_selector_all (
8218 document
, "blockquote[type=cite] > [data-evo-paragraph]", NULL
);
8219 for (ii
= webkit_dom_node_list_get_length (paragraphs
); ii
--;) {
8220 WebKitDOMNode
*paragraph
;
8222 paragraph
= webkit_dom_node_list_item (paragraphs
, ii
);
8224 e_editor_dom_wrap_paragraph (editor_page
, WEBKIT_DOM_ELEMENT (paragraph
));
8226 g_clear_object (¶graphs
);
8230 process_content_to_plain_text_changing_composer_mode (EEditorPage
*editor_page
)
8232 WebKitDOMDocument
*document
;
8233 WebKitDOMNode
*body
, *head
, *node
;
8234 WebKitDOMElement
*blockquote
;
8236 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8238 document
= e_editor_page_get_document (editor_page
);
8239 body
= WEBKIT_DOM_NODE (webkit_dom_document_get_body (document
));
8240 head
= WEBKIT_DOM_NODE (webkit_dom_document_get_head (document
));
8242 while ((node
= webkit_dom_node_get_last_child (head
)))
8245 e_editor_dom_selection_save (editor_page
);
8247 webkit_dom_element_remove_attribute (
8248 WEBKIT_DOM_ELEMENT (body
), "data-user-colors");
8250 e_editor_page_emit_user_changed_default_colors (editor_page
, FALSE
);
8252 webkit_dom_element_set_attribute (
8253 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text", "", NULL
);
8255 blockquote
= webkit_dom_document_query_selector (
8256 document
, "blockquote[type|=cite]", NULL
);
8259 wrap_paragraphs_in_quoted_content (editor_page
);
8260 preserve_pre_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (body
));
8261 quote_plain_text_elements_after_wrapping_in_document (editor_page
);
8264 toggle_paragraphs_style (editor_page
);
8265 toggle_smileys (editor_page
);
8266 toggle_indented_elements (editor_page
);
8267 remove_images (document
);
8268 remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body
));
8270 process_node_to_plain_text_changing_composer_mode (editor_page
, body
);
8272 e_editor_dom_selection_restore (editor_page
);
8273 e_editor_dom_force_spell_check_in_viewport (editor_page
);
8277 e_editor_dom_process_content_to_plain_text_for_exporting (EEditorPage
*editor_page
)
8279 WebKitDOMDocument
*document
;
8280 WebKitDOMElement
*element
;
8281 WebKitDOMNode
*body
, *source
;
8282 WebKitDOMNodeList
*list
= NULL
;
8283 WebKitDOMDOMWindow
*dom_window
= NULL
;
8284 WebKitDOMDOMSelection
*dom_selection
= NULL
;
8285 gboolean wrap
= TRUE
, quote
= FALSE
, remove_last_new_line
= FALSE
;
8287 GString
*plain_text
;
8289 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
8291 document
= e_editor_page_get_document (editor_page
);
8292 plain_text
= g_string_sized_new (1024);
8294 body
= WEBKIT_DOM_NODE (webkit_dom_document_get_body (document
));
8295 source
= webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body
), TRUE
, NULL
);
8297 e_editor_dom_selection_save (editor_page
);
8299 /* If composer is in HTML mode we have to move the content to plain version */
8300 if (e_editor_page_get_html_mode (editor_page
)) {
8301 if (e_editor_dom_check_if_conversion_needed (editor_page
)) {
8302 WebKitDOMElement
*wrapper
;
8303 WebKitDOMNode
*child
, *last_child
;
8305 wrapper
= webkit_dom_document_create_element (document
, "div", NULL
);
8306 webkit_dom_element_set_attribute (
8307 WEBKIT_DOM_ELEMENT (wrapper
),
8308 "data-evo-html-to-plain-text-wrapper",
8311 while ((child
= webkit_dom_node_get_first_child (source
))) {
8312 webkit_dom_node_append_child (
8313 WEBKIT_DOM_NODE (wrapper
),
8318 list
= webkit_dom_element_query_selector_all (
8319 wrapper
, "#-x-evo-input-start", NULL
);
8320 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8321 WebKitDOMNode
*paragraph
;
8323 paragraph
= webkit_dom_node_list_item (list
, ii
);
8325 webkit_dom_element_remove_attribute (
8326 WEBKIT_DOM_ELEMENT (paragraph
), "id");
8328 g_clear_object (&list
);
8330 remove_images_in_element (wrapper
);
8332 list
= webkit_dom_element_query_selector_all (
8333 wrapper
, "[data-evo-html-to-plain-text-wrapper] > :matches(ul, ol)", NULL
);
8334 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8335 WebKitDOMElement
*list_pre
;
8336 WebKitDOMNode
*item
;
8337 GString
*list_plain_text
;
8339 item
= webkit_dom_node_list_item (list
, ii
);
8341 list_plain_text
= g_string_new ("");
8343 process_list_to_plain_text (
8344 editor_page
, WEBKIT_DOM_ELEMENT (item
), 1, list_plain_text
);
8346 list_pre
= webkit_dom_document_create_element (document
, "pre", NULL
);
8347 webkit_dom_html_element_set_inner_text (
8348 WEBKIT_DOM_HTML_ELEMENT (list_pre
),
8349 list_plain_text
->str
,
8351 webkit_dom_node_replace_child (
8352 WEBKIT_DOM_NODE (wrapper
),
8353 WEBKIT_DOM_NODE (list_pre
),
8357 g_string_free (list_plain_text
, TRUE
);
8359 g_clear_object (&list
);
8361 /* BR on the end of the last element would cause an extra
8362 * new line, remove it if there are some nodes before it. */
8363 last_child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (wrapper
));
8364 while (webkit_dom_node_get_last_child (last_child
))
8365 last_child
= webkit_dom_node_get_last_child (last_child
);
8367 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child
) &&
8368 webkit_dom_node_get_previous_sibling (last_child
))
8369 remove_node (last_child
);
8371 convert_element_from_html_to_plain_text (
8372 editor_page
, wrapper
, &wrap
, "e
);
8374 source
= WEBKIT_DOM_NODE (wrapper
);
8376 remove_last_new_line
= TRUE
;
8378 toggle_paragraphs_style_in_element (
8379 editor_page
, WEBKIT_DOM_ELEMENT (source
), FALSE
);
8380 remove_images_in_element (
8381 WEBKIT_DOM_ELEMENT (source
));
8382 remove_background_images_in_element (
8383 WEBKIT_DOM_ELEMENT (source
));
8387 list
= webkit_dom_element_query_selector_all (
8388 WEBKIT_DOM_ELEMENT (source
), "[data-evo-paragraph]", NULL
);
8390 dom_window
= webkit_dom_document_get_default_view (document
);
8391 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
8392 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
8393 g_clear_object (&dom_window
);
8394 g_clear_object (&dom_selection
);
8396 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8397 WebKitDOMNode
*paragraph
;
8399 paragraph
= webkit_dom_node_list_item (list
, ii
);
8401 if (node_is_list (paragraph
)) {
8402 WebKitDOMNode
*item
= webkit_dom_node_get_first_child (paragraph
);
8405 WebKitDOMNode
*next_item
=
8406 webkit_dom_node_get_next_sibling (item
);
8408 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
))
8409 e_editor_dom_wrap_paragraph (editor_page
, WEBKIT_DOM_ELEMENT (item
));
8413 } else if (!webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (paragraph
), ".-x-evo-wrap-br,.-x-evo-quoted", NULL
)) {
8414 /* Don't try to wrap the already wrapped content. */
8415 e_editor_dom_wrap_paragraph (editor_page
, WEBKIT_DOM_ELEMENT (paragraph
));
8418 g_clear_object (&list
);
8420 if ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker")))
8421 remove_node (WEBKIT_DOM_NODE (element
));
8422 if ((element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-end-marker")))
8423 remove_node (WEBKIT_DOM_NODE (element
));
8425 webkit_dom_node_normalize (source
);
8428 quote_plain_text_elements_after_wrapping_in_element (editor_page
, WEBKIT_DOM_ELEMENT (source
));
8429 } else if (e_editor_page_get_html_mode (editor_page
)) {
8430 WebKitDOMElement
*citation
;
8432 citation
= webkit_dom_element_query_selector (
8433 WEBKIT_DOM_ELEMENT (source
), "blockquote[type=cite]", NULL
);
8435 preserve_pre_line_breaks_in_element (document
, WEBKIT_DOM_ELEMENT (source
));
8436 quote_plain_text_elements_after_wrapping_in_element (editor_page
, WEBKIT_DOM_ELEMENT (source
));
8440 process_node_to_plain_text_for_exporting (editor_page
, source
, plain_text
);
8441 /* Truncate the extra new line on the end of generated text as the
8442 * check inside the previous function is based on whether the processed
8443 * node is BODY or not, but in this case the content is wrapped in DIV. */
8444 if (remove_last_new_line
)
8445 g_string_truncate (plain_text
, plain_text
->len
- 1);
8447 e_editor_dom_selection_restore (editor_page
);
8449 /* Return text content between <body> and </body> */
8450 return g_string_free (plain_text
, FALSE
);
8454 restore_image (WebKitDOMDocument
*document
,
8456 const gchar
*element_src
)
8460 WebKitDOMNodeList
*list
= NULL
;
8462 selector
= g_strconcat ("[data-inline][background=\"cid:", id
, "\"]", NULL
);
8463 list
= webkit_dom_document_query_selector_all (document
, selector
, NULL
);
8464 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8465 WebKitDOMElement
*element
= WEBKIT_DOM_ELEMENT (
8466 webkit_dom_node_list_item (list
, ii
));
8468 webkit_dom_element_set_attribute (element
, "background", element_src
, NULL
);
8471 g_clear_object (&list
);
8473 selector
= g_strconcat ("[data-inline][src=\"cid:", id
, "\"]", NULL
);
8474 list
= webkit_dom_document_query_selector_all (document
, selector
, NULL
);
8475 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8476 WebKitDOMElement
*element
= WEBKIT_DOM_ELEMENT (
8477 webkit_dom_node_list_item (list
, ii
));
8479 webkit_dom_element_set_attribute (element
, "src", element_src
, NULL
);
8482 g_clear_object (&list
);
8486 e_editor_dom_restore_images (EEditorPage
*editor_page
,
8487 GVariant
*inline_images_to_restore
)
8489 WebKitDOMDocument
*document
;
8490 const gchar
*element_src
, *name
, *id
;
8493 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8495 document
= e_editor_page_get_document (editor_page
);
8497 g_variant_get (inline_images_to_restore
, "a(sss)", &iter
);
8498 while (g_variant_iter_loop (iter
, "(&s&s&s)", &element_src
, &name
, &id
))
8499 restore_image (document
, id
, element_src
);
8501 g_variant_iter_free (iter
);
8505 e_editor_dom_process_content_to_html_for_exporting (EEditorPage
*editor_page
)
8507 WebKitDOMDocument
*document
;
8508 WebKitDOMElement
*element
;
8509 WebKitDOMNode
*node
, *document_clone
;
8510 WebKitDOMNodeList
*list
= NULL
;
8511 GSettings
*settings
;
8513 gchar
*html_content
;
8514 gboolean send_editor_colors
= FALSE
;
8516 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
8518 document
= e_editor_page_get_document (editor_page
);
8520 document_clone
= webkit_dom_node_clone_node_with_error (
8521 WEBKIT_DOM_NODE (webkit_dom_document_get_document_element (document
)), TRUE
, NULL
);
8522 element
= webkit_dom_element_query_selector (
8523 WEBKIT_DOM_ELEMENT (document_clone
), "style#-x-evo-quote-style", NULL
);
8525 remove_node (WEBKIT_DOM_NODE (element
));
8526 element
= webkit_dom_element_query_selector (
8527 WEBKIT_DOM_ELEMENT (document_clone
), "style#-x-evo-a-color-style", NULL
);
8529 remove_node (WEBKIT_DOM_NODE (element
));
8530 element
= webkit_dom_element_query_selector (
8531 WEBKIT_DOM_ELEMENT (document_clone
), "style#-x-evo-a-color-style-visited", NULL
);
8533 remove_node (WEBKIT_DOM_NODE (element
));
8534 /* When the Ctrl + Enter is pressed for sending, the links are activated. */
8535 element
= webkit_dom_element_query_selector (
8536 WEBKIT_DOM_ELEMENT (document_clone
), "style#-x-evo-style-a", NULL
);
8538 remove_node (WEBKIT_DOM_NODE (element
));
8539 node
= WEBKIT_DOM_NODE (webkit_dom_element_query_selector (
8540 WEBKIT_DOM_ELEMENT (document_clone
), "body", NULL
));
8541 element
= webkit_dom_element_query_selector (
8542 WEBKIT_DOM_ELEMENT (node
), "#-x-evo-selection-start-marker", NULL
);
8544 remove_node (WEBKIT_DOM_NODE (element
));
8545 element
= webkit_dom_element_query_selector (
8546 WEBKIT_DOM_ELEMENT (node
), "#-x-evo-selection-end-marker", NULL
);
8548 remove_node (WEBKIT_DOM_NODE (element
));
8550 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
8551 send_editor_colors
= g_settings_get_boolean (settings
, "composer-inherit-theme-colors");
8552 g_object_unref (settings
);
8554 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node
), "data-user-colors")) {
8555 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "data-user-colors");
8556 } else if (!send_editor_colors
) {
8557 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "bgcolor");
8558 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "text");
8559 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "link");
8560 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node
), "vlink");
8563 list
= webkit_dom_element_query_selector_all (
8564 WEBKIT_DOM_ELEMENT (node
), "span[data-hidden-space]", NULL
);
8565 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;)
8566 remove_node (webkit_dom_node_list_item (list
, ii
));
8567 g_clear_object (&list
);
8569 list
= webkit_dom_element_query_selector_all (
8570 WEBKIT_DOM_ELEMENT (node
), "[data-style]", NULL
);
8571 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8572 WebKitDOMNode
*data_style_node
;
8574 data_style_node
= webkit_dom_node_list_item (list
, ii
);
8576 element_rename_attribute (WEBKIT_DOM_ELEMENT (data_style_node
), "data-style", "style");
8578 g_clear_object (&list
);
8580 style_blockquotes (WEBKIT_DOM_ELEMENT (node
));
8581 process_node_to_html_for_exporting (editor_page
, node
);
8583 html_content
= webkit_dom_element_get_outer_html (
8584 WEBKIT_DOM_ELEMENT (document_clone
));
8586 if (strstr (html_content
, UNICODE_ZERO_WIDTH_SPACE
)) {
8589 processed
= e_str_replace_string (html_content
, UNICODE_ZERO_WIDTH_SPACE
, "");
8590 g_free (html_content
);
8591 html_content
= g_string_free (processed
, FALSE
);
8594 return html_content
;
8598 e_editor_dom_convert_when_changing_composer_mode (EEditorPage
*editor_page
)
8600 WebKitDOMDocument
*document
;
8601 WebKitDOMHTMLElement
*body
;
8602 gboolean quote
= FALSE
, wrap
= FALSE
;
8604 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8606 document
= e_editor_page_get_document (editor_page
);
8607 body
= webkit_dom_document_get_body (document
);
8609 convert_element_from_html_to_plain_text (
8610 editor_page
, WEBKIT_DOM_ELEMENT (body
), &wrap
, "e
);
8613 e_editor_dom_wrap_paragraphs_in_document (editor_page
);
8616 e_editor_dom_selection_save (editor_page
);
8618 quote_plain_text_elements_after_wrapping_in_document (editor_page
);
8620 body
= WEBKIT_DOM_HTML_ELEMENT (dom_quote_plain_text (document
));
8621 e_editor_dom_selection_restore (editor_page
);
8624 toggle_paragraphs_style (editor_page
);
8625 toggle_smileys (editor_page
);
8626 remove_images (document
);
8627 remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body
));
8629 clear_attributes (editor_page
);
8631 if (!e_editor_page_get_html_mode (editor_page
))
8632 webkit_dom_element_set_attribute (
8633 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text", "", NULL
);
8635 webkit_dom_element_remove_attribute (
8636 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text");
8638 e_editor_dom_force_spell_check_in_viewport (editor_page
);
8639 e_editor_dom_scroll_to_caret (editor_page
);
8643 set_base64_to_element_attribute (GHashTable
*inline_images
,
8644 WebKitDOMElement
*element
,
8645 const gchar
*attribute
)
8647 gchar
*attribute_value
;
8648 const gchar
*base64_src
;
8650 attribute_value
= webkit_dom_element_get_attribute (element
, attribute
);
8652 if (attribute_value
&& (base64_src
= g_hash_table_lookup (inline_images
, attribute_value
)) != NULL
) {
8653 const gchar
*base64_data
= strstr (base64_src
, ";") + 1;
8658 g_utf8_strlen (base64_src
, -1) -
8659 g_utf8_strlen (base64_data
, -1) - 1;
8660 name
= g_strndup (base64_src
, name_length
);
8662 webkit_dom_element_set_attribute (element
, "data-inline", "", NULL
);
8663 webkit_dom_element_set_attribute (element
, "data-name", name
, NULL
);
8664 webkit_dom_element_set_attribute (element
, attribute
, base64_data
, NULL
);
8668 g_free (attribute_value
);
8672 change_cid_images_src_to_base64 (EEditorPage
*editor_page
)
8674 WebKitDOMDocument
*document
;
8675 WebKitDOMElement
*document_element
;
8676 WebKitDOMNamedNodeMap
*attributes
= NULL
;
8677 WebKitDOMNodeList
*list
= NULL
;
8678 GHashTable
*inline_images
;
8681 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8683 document
= e_editor_page_get_document (editor_page
);
8684 inline_images
= e_editor_page_get_inline_images (editor_page
);
8686 document_element
= webkit_dom_document_get_document_element (document
);
8688 list
= webkit_dom_document_query_selector_all (document
, "img[src^=\"cid:\"]", NULL
);
8689 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8690 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
8692 set_base64_to_element_attribute (inline_images
, WEBKIT_DOM_ELEMENT (node
), "src");
8694 g_clear_object (&list
);
8697 attributes
= webkit_dom_element_get_attributes (document_element
);
8698 length
= webkit_dom_named_node_map_get_length (attributes
);
8699 for (ii
= 0; ii
< length
; ii
++) {
8701 WebKitDOMAttr
*attribute
= WEBKIT_DOM_ATTR( webkit_dom_named_node_map_item (attributes
, ii
));
8703 name
= webkit_dom_attr_get_name (attribute
);
8705 if (g_str_has_prefix (name
, "xmlns:")) {
8706 const gchar
*ns
= name
+ 6;
8707 gchar
*attribute_ns
= g_strconcat (ns
, ":src", NULL
);
8708 gchar
*selector
= g_strconcat ("img[", ns
, "\\:src^=\"cid:\"]", NULL
);
8711 list
= webkit_dom_document_query_selector_all (
8712 document
, selector
, NULL
);
8713 for (jj
= webkit_dom_node_list_get_length (list
); jj
--;) {
8714 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, jj
);
8716 set_base64_to_element_attribute (
8717 inline_images
, WEBKIT_DOM_ELEMENT (node
), attribute_ns
);
8720 g_clear_object (&list
);
8721 g_free (attribute_ns
);
8726 g_clear_object (&attributes
);
8728 list
= webkit_dom_document_query_selector_all (
8729 document
, "[background^=\"cid:\"]", NULL
);
8730 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8731 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
8733 set_base64_to_element_attribute (
8734 inline_images
, WEBKIT_DOM_ELEMENT (node
), "background");
8736 g_clear_object (&list
);
8740 split_div_into_paragraphs (EEditorPage
*editor_page
,
8741 WebKitDOMDocument
*document
,
8742 WebKitDOMNode
*element
,
8743 WebKitDOMNode
*parent
)
8745 WebKitDOMNode
*node
, *new_div
= NULL
;
8747 if (!element
|| !parent
|| !WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element
))
8750 node
= webkit_dom_node_get_first_child (element
);
8752 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
8754 new_div
= WEBKIT_DOM_NODE (e_editor_dom_get_paragraph_element (editor_page
, -1, 0));
8755 webkit_dom_node_insert_before (parent
, new_div
, element
, NULL
);
8756 webkit_dom_node_append_child (new_div
, webkit_dom_node_clone_node_with_error (node
, TRUE
, NULL
), NULL
);
8760 } else if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (node
) ||
8761 WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node
) ||
8762 WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node
)) {
8764 webkit_dom_node_insert_before (parent
, webkit_dom_node_clone_node_with_error (node
, TRUE
, NULL
), element
, NULL
);
8767 new_div
= WEBKIT_DOM_NODE (e_editor_dom_get_paragraph_element (editor_page
, -1, 0));
8768 webkit_dom_node_insert_before (parent
, new_div
, element
, NULL
);
8771 webkit_dom_node_append_child (new_div
, webkit_dom_node_clone_node_with_error (node
, TRUE
, NULL
), NULL
);
8774 node
= webkit_dom_node_get_next_sibling (node
);
8777 webkit_dom_node_remove_child (parent
, element
, NULL
);
8781 e_editor_dom_adapt_to_editor_dom_changes (EEditorPage
*editor_page
)
8783 WebKitDOMDocument
*document
;
8784 WebKitDOMHTMLCollection
*collection
= NULL
;
8787 document
= e_editor_page_get_document (editor_page
);
8789 /* Normal block code div.-x-evo-paragraph replaced by div[data-evo-paragraph] */
8790 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (document
, "-x-evo-paragraph");
8791 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
8792 WebKitDOMNode
*node
;
8793 WebKitDOMElement
*parent_element
;
8795 node
= webkit_dom_html_collection_item (collection
, ii
);
8796 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-paragraph");
8797 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-evo-paragraph", "", NULL
);
8799 parent_element
= webkit_dom_node_get_parent_element (node
);
8802 split_div_into_paragraphs (editor_page
, document
, node
, WEBKIT_DOM_NODE (parent_element
));
8804 g_clear_object (&collection
);
8808 e_editor_dom_process_content_after_load (EEditorPage
*editor_page
)
8811 gint16 start_at_bottom
= -1, top_signature
= -1;
8812 WebKitDOMDocument
*document
;
8813 WebKitDOMHTMLElement
*body
;
8814 WebKitDOMDOMWindow
*dom_window
= NULL
;
8816 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8818 document
= e_editor_page_get_document (editor_page
);
8820 /* Don't use CSS when possible to preserve compatibility with older
8821 * versions of Evolution or other MUAs */
8822 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS
, "false");
8823 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR
, "div");
8825 body
= webkit_dom_document_get_body (document
);
8827 webkit_dom_element_remove_attribute (webkit_dom_document_get_document_element (document
), "dir");
8828 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body
), "style");
8829 html_mode
= e_editor_page_get_html_mode (editor_page
);
8831 webkit_dom_element_set_attribute (
8832 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text", "", NULL
);
8834 if (e_editor_page_get_convert_in_situ (editor_page
, &start_at_bottom
, &top_signature
)) {
8835 e_editor_dom_convert_content (editor_page
, NULL
, start_at_bottom
, top_signature
);
8836 /* The BODY could be replaced during the conversion */
8837 body
= webkit_dom_document_get_body (document
);
8838 /* Make the quote marks non-selectable. */
8839 e_editor_dom_disable_quote_marks_select (editor_page
);
8840 dom_set_links_active (document
, FALSE
);
8841 e_editor_page_set_convert_in_situ (editor_page
, FALSE
, -1, -1);
8843 /* The composer body could be empty in some case (loading an empty string
8844 * or empty HTML). In that case create the initial paragraph. */
8845 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
))) {
8846 WebKitDOMElement
*paragraph
;
8848 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
8849 webkit_dom_element_set_id (paragraph
, "-x-evo-input-start");
8850 webkit_dom_node_append_child (
8851 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (paragraph
), NULL
);
8852 e_editor_dom_selection_restore (editor_page
);
8857 WebKitDOMNodeList
*list
;
8860 list
= webkit_dom_document_query_selector_all (document
, "pre", NULL
);
8861 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8862 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
), *parent
;
8863 WebKitDOMElement
*element
;
8866 element
= WEBKIT_DOM_ELEMENT (node
);
8867 parent
= webkit_dom_node_get_parent_node (node
);
8868 inner_html
= webkit_dom_element_get_inner_html (element
);
8870 if (inner_html
&& *inner_html
) {
8873 strv
= g_strsplit (inner_html
, "\n", -1);
8874 if (strv
&& strv
[0] && strv
[1]) {
8875 WebKitDOMElement
*pre
;
8878 for (jj
= 0; strv
[jj
]; jj
++) {
8879 pre
= webkit_dom_document_create_element (document
, "pre", NULL
);
8881 gint len
= strlen (strv
[jj
]);
8883 if (strv
[jj
][len
- 1] == '\r') {
8884 strv
[jj
][len
- 1] = '\0';
8889 webkit_dom_html_element_set_inner_html (WEBKIT_DOM_HTML_ELEMENT (pre
), strv
[jj
], NULL
);
8891 WebKitDOMElement
*br
;
8893 br
= webkit_dom_document_create_element (document
, "br", NULL
);
8894 webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre
), WEBKIT_DOM_NODE (br
), NULL
);
8897 webkit_dom_node_insert_before (parent
, WEBKIT_DOM_NODE (pre
), node
, NULL
);
8906 g_free (inner_html
);
8909 g_clear_object (&list
);
8912 e_editor_dom_adapt_to_editor_dom_changes (editor_page
);
8914 /* Make the quote marks non-selectable. */
8915 e_editor_dom_disable_quote_marks_select (editor_page
);
8916 dom_set_links_active (document
, FALSE
);
8917 put_body_in_citation (document
);
8918 move_elements_to_body (editor_page
);
8919 repair_blockquotes (document
);
8920 remove_thunderbird_signature (document
);
8922 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body
), "data-evo-draft")) {
8923 /* Restore the selection how it was when the draft was saved */
8924 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (body
), FALSE
);
8925 e_editor_dom_selection_restore (editor_page
);
8926 e_editor_dom_remove_embedded_style_sheet (editor_page
);
8929 /* The composer body could be empty in some case (loading an empty string
8930 * or empty HTML. In that case create the initial paragraph. */
8931 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
))) {
8932 WebKitDOMElement
*paragraph
;
8934 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
8935 webkit_dom_element_set_id (paragraph
, "-x-evo-input-start");
8936 webkit_dom_node_append_child (
8937 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (paragraph
), NULL
);
8938 e_editor_dom_selection_restore (editor_page
);
8941 e_editor_dom_fix_file_uri_images (editor_page
);
8942 change_cid_images_src_to_base64 (editor_page
);
8945 webkit_dom_element_set_attribute (webkit_dom_document_get_document_element (document
), "dir", "ltr", NULL
);
8946 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (body
), "style", "text-align:left; direction:ltr;", NULL
);
8948 /* Register on input event that is called when the content (body) is modified */
8949 e_editor_dom_register_input_event_listener_on_body (editor_page
);
8950 register_html_events_handlers (editor_page
, body
);
8952 if (e_editor_page_get_inline_spelling_enabled (editor_page
))
8953 e_editor_dom_force_spell_check_in_viewport (editor_page
);
8955 e_editor_dom_turn_spell_check_off (editor_page
);
8957 e_editor_dom_scroll_to_caret (editor_page
);
8959 dom_window
= webkit_dom_document_get_default_view (document
);
8961 webkit_dom_event_target_add_event_listener (
8962 WEBKIT_DOM_EVENT_TARGET (dom_window
),
8964 G_CALLBACK (body_scroll_event_cb
),
8968 /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the
8969 * callback won't be set up. */
8973 encode_to_base64_data (const gchar
*src_uri
,
8978 gchar
*filename
, *data
= NULL
;
8980 g_return_val_if_fail (src_uri
!= NULL
, NULL
);
8982 file
= g_file_new_for_uri (src_uri
);
8986 filename
= g_file_get_path (file
);
8988 g_object_unref (file
);
8992 info
= g_file_query_info (file
, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
"," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
,
8993 G_FILE_QUERY_INFO_NONE
, NULL
, NULL
);
8996 gchar
*mime_type
, *content
= NULL
;
8999 mime_type
= g_content_type_get_mime_type (g_file_info_get_content_type (info
));
9001 if (mime_type
&& g_file_get_contents (filename
, &content
, &length
, NULL
)) {
9002 gchar
*base64_encoded
;
9005 *data_name
= g_strdup (g_file_info_get_display_name (info
));
9007 base64_encoded
= g_base64_encode ((const guchar
*) content
, length
);
9008 data
= g_strconcat ("data:", mime_type
, ";base64,", base64_encoded
, NULL
);
9009 g_free (base64_encoded
);
9012 g_clear_object (&info
);
9017 g_clear_object (&file
);
9024 e_editor_dom_get_inline_images_data (EEditorPage
*editor_page
,
9025 const gchar
*uid_domain
)
9027 WebKitDOMDocument
*document
;
9028 WebKitDOMNodeList
*list
= NULL
;
9029 GVariant
*result
= NULL
;
9030 GVariantBuilder
*builder
= NULL
;
9031 GHashTable
*added
= NULL
;
9034 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
9036 document
= e_editor_page_get_document (editor_page
);
9037 list
= webkit_dom_document_query_selector_all (document
, "img[src]", NULL
);
9039 length
= webkit_dom_node_list_get_length (list
);
9041 g_clear_object (&list
);
9045 builder
= g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
9047 added
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
9048 for (ii
= length
; ii
--;) {
9051 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
9052 gchar
*src
= webkit_dom_element_get_attribute (
9053 WEBKIT_DOM_ELEMENT (node
), "src");
9058 if ((id
= g_hash_table_lookup (added
, src
)) != NULL
) {
9059 cid
= g_strdup_printf ("cid:%s", id
);
9060 } else if (g_ascii_strncasecmp (src
, "data:", 5) == 0) {
9061 gchar
*data_name
= webkit_dom_element_get_attribute (
9062 WEBKIT_DOM_ELEMENT (node
), "data-name");
9067 new_id
= camel_header_msgid_generate (uid_domain
);
9068 g_variant_builder_add (
9069 builder
, "(sss)", src
, data_name
, new_id
);
9070 cid
= g_strdup_printf ("cid:%s", new_id
);
9072 g_hash_table_insert (added
, g_strdup (src
), new_id
);
9074 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-inline", "", NULL
);
9077 } else if (g_ascii_strncasecmp (src
, "file://", 7) == 0) {
9078 gchar
*data
, *data_name
= NULL
;
9080 data
= encode_to_base64_data (src
, &data_name
);
9082 if (data
&& data_name
) {
9085 new_id
= camel_header_msgid_generate (uid_domain
);
9086 g_variant_builder_add (builder
, "(sss)", data
, data_name
, new_id
);
9087 cid
= g_strdup_printf ("cid:%s", new_id
);
9089 g_hash_table_insert (added
, data
, new_id
);
9091 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-name", data_name
, NULL
);
9092 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-inline", "", NULL
);
9101 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "src", cid
, NULL
);
9107 g_clear_object (&list
);
9110 list
= webkit_dom_document_query_selector_all (
9111 document
, "[data-inline][background]", NULL
);
9112 length
= webkit_dom_node_list_get_length (list
);
9116 builder
= g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
9118 added
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
9120 for (ii
= length
; ii
--;) {
9123 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
9124 gchar
*src
= webkit_dom_element_get_attribute (
9125 WEBKIT_DOM_ELEMENT (node
), "background");
9130 if ((id
= g_hash_table_lookup (added
, src
)) != NULL
) {
9131 cid
= g_strdup_printf ("cid:%s", id
);
9132 webkit_dom_element_set_attribute (
9133 WEBKIT_DOM_ELEMENT (node
), "background", cid
, NULL
);
9136 gchar
*data_name
= webkit_dom_element_get_attribute (
9137 WEBKIT_DOM_ELEMENT (node
), "data-name");
9142 new_id
= camel_header_msgid_generate (uid_domain
);
9143 g_variant_builder_add (
9144 builder
, "(sss)", src
, data_name
, new_id
);
9145 cid
= g_strdup_printf ("cid:%s", new_id
);
9147 g_hash_table_insert (added
, src
, new_id
);
9149 webkit_dom_element_set_attribute (
9150 WEBKIT_DOM_ELEMENT (node
), "background", cid
, NULL
);
9157 g_clear_object (&list
);
9159 g_hash_table_destroy (added
);
9162 result
= g_variant_new ("a(sss)", builder
);
9163 g_variant_builder_unref (builder
);
9170 pasting_quoted_content (const gchar
*content
)
9172 /* Check if the content we are pasting is a quoted content from composer.
9173 * If it is, we can't use WebKit to paste it as it would leave the formatting
9174 * on the content. */
9175 return g_str_has_prefix (
9177 "<meta http-equiv=\"content-type\" content=\"text/html; "
9178 "charset=utf-8\"><blockquote type=\"cite\"") &&
9179 strstr (content
, "\"-x-evo-");
9183 remove_apple_interchange_newline_elements (WebKitDOMDocument
*document
)
9186 WebKitDOMHTMLCollection
*collection
= NULL
;
9188 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (
9189 document
, "Apple-interchange-newline");
9190 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
9191 WebKitDOMNode
*node
= webkit_dom_html_collection_item (collection
, ii
);
9195 g_clear_object (&collection
);
9199 * e_editor_dom_insert_html:
9200 * @selection: an #EEditorSelection
9201 * @html_text: an HTML code to insert
9203 * Insert @html_text into document at current cursor position. When a text range
9204 * is selected, it will be replaced by @html_text.
9207 e_editor_dom_insert_html (EEditorPage
*editor_page
,
9208 const gchar
*html_text
)
9210 EEditorHistoryEvent
*ev
= NULL
;
9211 EEditorUndoRedoManager
*manager
;
9212 gboolean html_mode
, undo_redo_in_progress
;
9213 WebKitDOMDocument
*document
;
9214 WebKitDOMNode
*block
= NULL
;
9216 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
9217 g_return_if_fail (html_text
!= NULL
);
9219 document
= e_editor_page_get_document (editor_page
);
9221 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9222 undo_redo_in_progress
= e_editor_undo_redo_manager_is_operation_in_progress (manager
);
9223 if (!undo_redo_in_progress
) {
9226 ev
= g_new0 (EEditorHistoryEvent
, 1);
9227 ev
->type
= HISTORY_INSERT_HTML
;
9229 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
9230 e_editor_dom_selection_get_coordinates (editor_page
,
9231 &ev
->before
.start
.x
,
9232 &ev
->before
.start
.y
,
9237 ev
->before
.end
.x
= ev
->before
.start
.x
;
9238 ev
->before
.end
.y
= ev
->before
.start
.y
;
9241 ev
->data
.string
.from
= NULL
;
9242 ev
->data
.string
.to
= g_strdup (html_text
);
9245 html_mode
= e_editor_page_get_html_mode (editor_page
);
9247 (e_editor_page_is_pasting_content_from_itself (editor_page
) &&
9248 !pasting_quoted_content (html_text
))) {
9249 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
9250 EEditorHistoryEvent
*event
;
9251 WebKitDOMDocumentFragment
*fragment
;
9252 WebKitDOMRange
*range
= NULL
;
9254 event
= g_new0 (EEditorHistoryEvent
, 1);
9255 event
->type
= HISTORY_DELETE
;
9257 range
= e_editor_dom_get_current_range (editor_page
);
9258 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
9259 g_clear_object (&range
);
9260 event
->data
.fragment
= g_object_ref (fragment
);
9262 e_editor_dom_selection_get_coordinates (editor_page
,
9263 &event
->before
.start
.x
,
9264 &event
->before
.start
.y
,
9265 &event
->before
.end
.x
,
9266 &event
->before
.end
.y
);
9268 event
->after
.start
.x
= event
->before
.start
.x
;
9269 event
->after
.start
.y
= event
->before
.start
.y
;
9270 event
->after
.end
.x
= event
->before
.start
.x
;
9271 event
->after
.end
.y
= event
->before
.start
.y
;
9273 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
9275 event
= g_new0 (EEditorHistoryEvent
, 1);
9276 event
->type
= HISTORY_AND
;
9278 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
9280 WebKitDOMElement
*selection_marker
;
9282 e_editor_dom_selection_save (editor_page
);
9284 /* If current block contains just the BR element, remove
9285 * it otherwise WebKit will create a new block (with
9286 * text node that will contain '\n') on the end of inserted
9287 * content. Also remember the block and remove it if it's
9288 * empty after we insert the content. */
9289 selection_marker
= webkit_dom_document_get_element_by_id (
9290 document
, "-x-evo-selection-start-marker");
9292 if (!e_editor_page_is_pasting_content_from_itself (editor_page
)) {
9293 if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker
))) {
9294 WebKitDOMNode
*sibling
;
9296 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker
));
9297 sibling
= webkit_dom_node_get_next_sibling (sibling
);
9298 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
))
9299 remove_node (sibling
);
9302 block
= e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_marker
));
9304 e_editor_dom_selection_restore (editor_page
);
9307 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_HTML
, html_text
);
9310 remove_node_if_empty (block
);
9312 e_editor_dom_fix_file_uri_images (editor_page
);
9314 if (strstr (html_text
, "id=\"-x-evo-selection-start-marker\""))
9315 e_editor_dom_selection_restore (editor_page
);
9317 e_editor_dom_check_magic_links (editor_page
, FALSE
);
9318 e_editor_dom_scroll_to_caret (editor_page
);
9319 e_editor_dom_force_spell_check_in_viewport (editor_page
);
9321 /* Don't save history in the underlying function. */
9322 if (!undo_redo_in_progress
)
9323 e_editor_undo_redo_manager_set_operation_in_progress (manager
, TRUE
);
9324 e_editor_dom_convert_and_insert_html_into_selection (editor_page
, html_text
, TRUE
);
9325 if (!undo_redo_in_progress
)
9326 e_editor_undo_redo_manager_set_operation_in_progress (manager
, FALSE
);
9329 remove_apple_interchange_newline_elements (document
);
9332 e_editor_dom_selection_get_coordinates (editor_page
,
9338 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9343 save_history_for_delete_or_backspace (EEditorPage
*editor_page
,
9344 gboolean delete_key
,
9345 gboolean control_key
)
9347 WebKitDOMDocument
*document
;
9348 WebKitDOMDocumentFragment
*fragment
= NULL
;
9349 WebKitDOMDOMWindow
*dom_window
= NULL
;
9350 WebKitDOMDOMSelection
*dom_selection
= NULL
;
9351 WebKitDOMRange
*range
= NULL
;
9352 EEditorHistoryEvent
*ev
= NULL
;
9353 EEditorUndoRedoManager
*manager
;
9355 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
9357 document
= e_editor_page_get_document (editor_page
);
9358 dom_window
= webkit_dom_document_get_default_view (document
);
9359 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
9360 g_clear_object (&dom_window
);
9362 if (!webkit_dom_dom_selection_get_range_count (dom_selection
)) {
9363 g_clear_object (&dom_selection
);
9367 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9369 /* Check if we can delete something */
9370 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
9371 WebKitDOMRange
*tmp_range
= NULL
;
9373 webkit_dom_dom_selection_modify (
9374 dom_selection
, "move", delete_key
? "right" : "left", "character");
9376 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9377 if (webkit_dom_range_compare_boundary_points (tmp_range
, WEBKIT_DOM_RANGE_END_TO_END
, range
, NULL
) == 0) {
9378 g_clear_object (&dom_selection
);
9379 g_clear_object (&range
);
9380 g_clear_object (&tmp_range
);
9385 webkit_dom_dom_selection_modify (
9386 dom_selection
, "move", delete_key
? "left" : "right", "character");
9388 g_clear_object (&tmp_range
);
9391 if (save_history_before_event_in_table (editor_page
, range
)) {
9392 g_clear_object (&range
);
9393 g_clear_object (&dom_selection
);
9397 ev
= g_new0 (EEditorHistoryEvent
, 1);
9398 ev
->type
= HISTORY_DELETE
;
9400 e_editor_dom_selection_save (editor_page
);
9402 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
9403 g_clear_object (&range
);
9404 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9406 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
9407 gboolean removing_from_anchor
= FALSE
;
9408 WebKitDOMRange
*range_clone
= NULL
;
9409 WebKitDOMNode
*node
;
9411 e_editor_page_block_selection_changed (editor_page
);
9413 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
9415 WebKitDOMRange
*tmp_range
= NULL
;
9417 /* Control + Delete/Backspace deletes previous/next word. */
9418 webkit_dom_dom_selection_modify (
9419 dom_selection
, "move", delete_key
? "right" : "left", "word");
9420 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9422 webkit_dom_range_set_end (
9424 webkit_dom_range_get_end_container (tmp_range
, NULL
),
9425 webkit_dom_range_get_end_offset (tmp_range
, NULL
),
9428 webkit_dom_range_set_start (
9430 webkit_dom_range_get_start_container (tmp_range
, NULL
),
9431 webkit_dom_range_get_start_offset (tmp_range
, NULL
),
9433 g_clear_object (&tmp_range
);
9435 typedef WebKitDOMNode
* (*GetSibling
)(WebKitDOMNode
*node
);
9436 WebKitDOMNode
*container
, *sibling
;
9437 WebKitDOMElement
*selection_marker
;
9439 GetSibling get_sibling
= delete_key
?
9440 webkit_dom_node_get_next_sibling
:
9441 webkit_dom_node_get_previous_sibling
;
9443 container
= webkit_dom_range_get_end_container (range_clone
, NULL
);
9444 sibling
= get_sibling (container
);
9446 selection_marker
= webkit_dom_document_get_element_by_id (
9449 "-x-evo-selection-end-marker" :
9450 "-x-evo-selection-start-marker");
9452 if (selection_marker
) {
9453 WebKitDOMNode
*tmp_sibling
;
9455 tmp_sibling
= get_sibling (WEBKIT_DOM_NODE (selection_marker
));
9456 if (!tmp_sibling
|| (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp_sibling
) &&
9457 !element_has_class (WEBKIT_DOM_ELEMENT (tmp_sibling
), "-x-evo-wrap-br")))
9458 sibling
= WEBKIT_DOM_NODE (selection_marker
);
9461 if (e_editor_dom_is_selection_position_node (sibling
)) {
9462 if ((node
= get_sibling (sibling
)))
9463 node
= get_sibling (node
);
9465 if (WEBKIT_DOM_IS_ELEMENT (node
) &&
9466 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node
), "data-hidden-space")) {
9467 fragment
= webkit_dom_document_create_document_fragment (document
);
9468 webkit_dom_node_append_child (
9469 WEBKIT_DOM_NODE (fragment
),
9471 webkit_dom_document_create_text_node (document
, " ")),
9473 } else if (delete_key
) {
9474 webkit_dom_range_set_start (
9475 range_clone
, node
, 0, NULL
);
9476 webkit_dom_range_set_end (
9477 range_clone
, node
, 1, NULL
);
9480 WebKitDOMRange
*tmp_range
= NULL
, *actual_range
= NULL
;
9482 actual_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9484 webkit_dom_dom_selection_modify (
9485 dom_selection
, "move", delete_key
? "right" : "left", "character");
9487 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9488 if (webkit_dom_range_compare_boundary_points (tmp_range
, WEBKIT_DOM_RANGE_END_TO_END
, actual_range
, NULL
) != 0) {
9489 WebKitDOMNode
*actual_block
;
9490 WebKitDOMNode
*tmp_block
;
9492 actual_block
= e_editor_dom_get_parent_block_node_from_child (container
);
9494 tmp_block
= delete_key
?
9495 webkit_dom_range_get_end_container (tmp_range
, NULL
) :
9496 webkit_dom_range_get_start_container (tmp_range
, NULL
);
9497 tmp_block
= e_editor_dom_get_parent_block_node_from_child (tmp_block
);
9499 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9500 webkit_dom_dom_selection_add_range (dom_selection
, actual_range
);
9503 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (actual_block
))
9504 actual_block
= webkit_dom_node_get_parent_node (actual_block
);
9506 fragment
= webkit_dom_document_create_document_fragment (document
);
9508 WebKitDOMNode
*clone
;
9510 clone
= webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
);
9511 if (e_editor_dom_get_citation_level (actual_block
) > 0)
9512 webkit_dom_element_set_attribute (
9513 WEBKIT_DOM_ELEMENT (clone
),
9517 webkit_dom_node_append_child (
9518 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9520 clone
= webkit_dom_node_clone_node_with_error (tmp_block
, TRUE
, NULL
);
9521 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9522 webkit_dom_element_set_attribute (
9523 WEBKIT_DOM_ELEMENT (clone
),
9527 webkit_dom_node_append_child (
9528 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9530 WebKitDOMNode
*clone
;
9532 clone
= webkit_dom_node_clone_node_with_error (tmp_block
, TRUE
, NULL
);
9533 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9534 webkit_dom_element_set_attribute (
9535 WEBKIT_DOM_ELEMENT (clone
),
9539 webkit_dom_node_append_child (
9540 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9542 clone
= webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
);
9543 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9544 webkit_dom_element_set_attribute (
9545 WEBKIT_DOM_ELEMENT (clone
),
9549 webkit_dom_node_append_child (
9550 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9553 G_OBJECT (fragment
),
9554 "history-concatenating-blocks",
9555 GINT_TO_POINTER (1));
9558 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9559 webkit_dom_dom_selection_add_range (dom_selection
, actual_range
);
9561 g_clear_object (&tmp_range
);
9562 g_clear_object (&actual_range
);
9567 /* FIXME This code is wrong for unicode smileys. */
9568 offset
= webkit_dom_range_get_start_offset (range_clone
, NULL
);
9571 webkit_dom_range_set_end (
9572 range_clone
, container
, offset
+ 1, NULL
);
9574 webkit_dom_range_set_start (
9575 range_clone
, container
, offset
- 1, NULL
);
9577 removing_from_anchor
= WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (
9578 webkit_dom_node_get_parent_node (container
));
9583 fragment
= webkit_dom_range_clone_contents (range_clone
, NULL
);
9584 if (removing_from_anchor
)
9586 G_OBJECT (fragment
),
9587 "history-removing-from-anchor",
9588 GINT_TO_POINTER (1));
9589 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
9592 e_editor_page_unblock_selection_changed (editor_page
);
9593 g_clear_object (&range
);
9594 g_clear_object (&range_clone
);
9595 g_clear_object (&dom_selection
);
9596 g_warning ("History event was not saved for %s key", delete_key
? "Delete" : "Backspace");
9597 e_editor_dom_selection_restore (editor_page
);
9603 ev
->after
.start
.x
= ev
->before
.start
.x
;
9604 ev
->after
.start
.y
= ev
->before
.start
.y
;
9605 ev
->after
.end
.x
= ev
->before
.end
.x
;
9606 ev
->after
.end
.y
= ev
->before
.end
.y
;
9608 webkit_dom_range_collapse (range_clone
, TRUE
, NULL
);
9609 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9610 webkit_dom_dom_selection_add_range (dom_selection
, range_clone
);
9612 gboolean selection_saved
= FALSE
;
9613 WebKitDOMRange
*tmp_range
= NULL
;
9615 if (webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker"))
9616 selection_saved
= TRUE
;
9618 if (selection_saved
)
9619 e_editor_dom_selection_restore (editor_page
);
9621 tmp_range
= webkit_dom_range_clone_range (range_clone
, NULL
);
9622 /* Prepare the selection to the right position after
9623 * delete and save it. */
9624 webkit_dom_range_collapse (range_clone
, TRUE
, NULL
);
9625 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9626 webkit_dom_dom_selection_add_range (dom_selection
, range_clone
);
9627 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9628 /* Restore the selection where it was before the
9629 * history event was saved. */
9630 webkit_dom_range_collapse (tmp_range
, FALSE
, NULL
);
9631 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9632 webkit_dom_dom_selection_add_range (dom_selection
, tmp_range
);
9633 g_clear_object (&tmp_range
);
9635 if (selection_saved
)
9636 e_editor_dom_selection_save (editor_page
);
9639 gboolean selection_saved
= FALSE
;
9641 if (webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker"))
9642 selection_saved
= TRUE
;
9644 if (selection_saved
)
9645 e_editor_dom_selection_restore (editor_page
);
9648 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9650 webkit_dom_dom_selection_modify (dom_selection
, "move", "left", "character");
9651 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9652 webkit_dom_dom_selection_modify (dom_selection
, "move", "right", "character");
9654 ev
->after
.end
.x
= ev
->after
.start
.x
;
9655 ev
->after
.end
.y
= ev
->after
.start
.y
;
9658 if (selection_saved
)
9659 e_editor_dom_selection_save (editor_page
);
9662 g_clear_object (&range_clone
);
9665 if (!WEBKIT_DOM_IS_ELEMENT (node
)) {
9666 webkit_dom_node_insert_before (
9667 WEBKIT_DOM_NODE (fragment
),
9669 dom_create_selection_marker (document
, FALSE
)),
9670 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
9672 webkit_dom_node_insert_before (
9673 WEBKIT_DOM_NODE (fragment
),
9675 dom_create_selection_marker (document
, TRUE
)),
9676 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
9680 if (!WEBKIT_DOM_IS_ELEMENT (node
)) {
9681 webkit_dom_node_append_child (
9682 WEBKIT_DOM_NODE (fragment
),
9684 dom_create_selection_marker (document
, TRUE
)),
9686 webkit_dom_node_append_child (
9687 WEBKIT_DOM_NODE (fragment
),
9689 dom_create_selection_marker (document
, FALSE
)),
9694 e_editor_page_unblock_selection_changed (editor_page
);
9696 WebKitDOMElement
*tmp_element
;
9697 WebKitDOMNode
*sibling
;
9699 ev
->after
.start
.x
= ev
->before
.start
.x
;
9700 ev
->after
.start
.y
= ev
->before
.start
.y
;
9701 ev
->after
.end
.x
= ev
->before
.start
.x
;
9702 ev
->after
.end
.y
= ev
->before
.start
.y
;
9704 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
9706 tmp_element
= webkit_dom_document_fragment_query_selector (
9707 fragment
, "#-x-evo-selection-start-marker", NULL
);
9709 remove_node (WEBKIT_DOM_NODE (tmp_element
));
9711 tmp_element
= webkit_dom_document_fragment_query_selector (
9712 fragment
, "#-x-evo-selection-end-marker", NULL
);
9714 remove_node (WEBKIT_DOM_NODE (tmp_element
));
9716 remove_empty_blocks (document
);
9718 /* Selection starts in the beginning of blockquote. */
9719 tmp_element
= webkit_dom_document_get_element_by_id (
9720 document
, "-x-evo-selection-start-marker");
9721 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element
));
9722 if (sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
9723 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted")) {
9724 WebKitDOMNode
*child
;
9726 tmp_element
= webkit_dom_document_get_element_by_id (
9727 document
, "-x-evo-selection-end-marker");
9729 /* If there is no text after the selection end it means that
9730 * the block will be replaced with block that is body's descendant
9731 * and not the blockquote's one. Also if the selection started
9732 * in the beginning of blockquote we have to insert the quote
9733 * characters into the deleted content to correctly restore
9734 * them during undo/redo operations. */
9735 if (!(tmp_element
&& webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element
)))) {
9736 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
9737 while (child
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child
))
9738 child
= webkit_dom_node_get_first_child (child
);
9740 child
= webkit_dom_node_get_first_child (child
);
9741 if (child
&& (WEBKIT_DOM_IS_TEXT (child
) ||
9742 (WEBKIT_DOM_IS_ELEMENT (child
) &&
9743 !element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-quoted")))) {
9744 webkit_dom_node_insert_before (
9745 webkit_dom_node_get_parent_node (child
),
9746 webkit_dom_node_clone_node_with_error (sibling
, TRUE
, NULL
),
9753 /* When we were cloning the range above and the range contained
9754 * quoted content there will still be blockquote missing in the
9755 * final range. Let's modify the fragment and add it there. */
9756 tmp_element
= webkit_dom_document_get_element_by_id (
9757 document
, "-x-evo-selection-end-marker");
9759 WebKitDOMNode
*node
;
9761 node
= WEBKIT_DOM_NODE (tmp_element
);
9762 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node
)))
9763 node
= webkit_dom_node_get_parent_node (node
);
9765 if (node
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
)) {
9766 WebKitDOMNode
*last_child
;
9768 last_child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment
));
9770 if (last_child
&& !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child
)) {
9771 WebKitDOMDocumentFragment
*tmp_fragment
;
9772 WebKitDOMNode
*clone
;
9774 tmp_fragment
= webkit_dom_document_create_document_fragment (document
);
9775 clone
= webkit_dom_node_clone_node_with_error (node
, FALSE
, NULL
);
9776 clone
= webkit_dom_node_append_child (
9777 WEBKIT_DOM_NODE (tmp_fragment
), clone
, NULL
);
9778 webkit_dom_node_append_child (clone
, WEBKIT_DOM_NODE (fragment
), NULL
);
9779 fragment
= tmp_fragment
;
9784 /* FIXME Ugly hack */
9785 /* If the deleted selection contained the signature (or at least its
9786 * part) replace it with the unchanged signature to correctly perform
9787 * undo operation. */
9788 tmp_element
= webkit_dom_document_fragment_query_selector (fragment
, ".-x-evo-signature-wrapper", NULL
);
9790 WebKitDOMElement
*signature
;
9792 signature
= webkit_dom_document_query_selector (document
, ".-x-evo-signature-wrapper", NULL
);
9794 webkit_dom_node_replace_child (
9795 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp_element
)),
9796 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (signature
), TRUE
, NULL
),
9797 WEBKIT_DOM_NODE (tmp_element
),
9803 g_clear_object (&range
);
9804 g_clear_object (&dom_selection
);
9806 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (delete_key
));
9807 g_object_set_data (G_OBJECT (fragment
), "history-control-key", GINT_TO_POINTER (control_key
));
9809 ev
->data
.fragment
= g_object_ref (fragment
);
9811 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9812 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9814 e_editor_dom_selection_restore (editor_page
);
9818 e_editor_dom_fix_structure_after_delete_before_quoted_content (EEditorPage
*editor_page
,
9820 gboolean control_key
,
9821 gboolean delete_key
)
9823 WebKitDOMDocument
*document
;
9824 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
9825 WebKitDOMNode
*block
, *node
;
9826 gboolean collapsed
= FALSE
;
9828 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9830 document
= e_editor_page_get_document (editor_page
);
9831 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
9833 e_editor_dom_selection_save (editor_page
);
9835 selection_start_marker
= webkit_dom_document_get_element_by_id (
9836 document
, "-x-evo-selection-start-marker");
9837 selection_end_marker
= webkit_dom_document_get_element_by_id (
9838 document
, "-x-evo-selection-end-marker");
9840 if (!selection_start_marker
|| !selection_end_marker
)
9844 WebKitDOMNode
*next_block
;
9846 block
= e_editor_dom_get_parent_block_node_from_child (
9847 WEBKIT_DOM_NODE (selection_start_marker
));
9849 next_block
= webkit_dom_node_get_next_sibling (block
);
9851 /* Next block is quoted content */
9852 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block
)) {
9853 e_editor_dom_selection_restore (editor_page
);
9857 /* Delete was pressed in block without any content */
9858 if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
))) {
9859 e_editor_dom_selection_restore (editor_page
);
9863 /* If there is just BR element go ahead */
9864 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
));
9865 if (node
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
9866 e_editor_dom_selection_restore (editor_page
);
9869 if (key_code
!= ~0) {
9870 e_editor_dom_selection_restore (editor_page
);
9871 save_history_for_delete_or_backspace (
9872 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
9874 e_editor_dom_selection_restore (editor_page
);
9876 /* Remove the empty block and move caret to the right place. */
9877 remove_node (block
);
9880 /* To the beginning of the next block. */
9881 while (next_block
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block
))
9882 next_block
= webkit_dom_node_get_first_child (next_block
);
9884 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-quoted"))
9885 next_block
= webkit_dom_node_get_next_sibling (next_block
);
9887 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (next_block
), TRUE
);
9889 WebKitDOMNode
*prev_block
;
9891 /* On the end of previous block. */
9892 prev_block
= webkit_dom_node_get_previous_sibling (next_block
);
9893 while (prev_block
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block
))
9894 prev_block
= webkit_dom_node_get_last_child (prev_block
);
9897 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (prev_block
), FALSE
);
9904 e_editor_dom_selection_restore (editor_page
);
9910 split_citation (EEditorPage
*editor_page
)
9912 WebKitDOMDocument
*document
;
9913 WebKitDOMElement
*element
;
9914 EEditorHistoryEvent
*ev
= NULL
;
9915 EEditorUndoRedoManager
*manager
;
9917 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9919 document
= e_editor_page_get_document (editor_page
);
9920 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9922 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
9923 WebKitDOMElement
*selection_end
;
9924 WebKitDOMNode
*sibling
;
9926 ev
= g_new0 (EEditorHistoryEvent
, 1);
9927 ev
->type
= HISTORY_CITATION_SPLIT
;
9929 e_editor_dom_selection_save (editor_page
);
9931 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
9933 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
9934 WebKitDOMRange
*range
= NULL
;
9936 range
= e_editor_dom_get_current_range (editor_page
);
9937 insert_delete_event (editor_page
, range
);
9939 g_clear_object (&range
);
9941 ev
->before
.end
.x
= ev
->before
.start
.x
;
9942 ev
->before
.end
.y
= ev
->before
.start
.y
;
9945 selection_end
= webkit_dom_document_get_element_by_id (
9946 document
, "-x-evo-selection-end-marker");
9948 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end
));
9949 if (!sibling
|| (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
9950 !element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-wrap-br"))) {
9951 WebKitDOMDocumentFragment
*fragment
;
9953 fragment
= webkit_dom_document_create_document_fragment (document
);
9954 ev
->data
.fragment
= g_object_ref (fragment
);
9956 ev
->data
.fragment
= NULL
;
9958 e_editor_dom_selection_restore (editor_page
);
9961 element
= e_editor_dom_insert_new_line_into_citation (editor_page
, "");
9964 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9966 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9969 return element
!= NULL
;
9973 delete_last_character_from_previous_line_in_quoted_block (EEditorPage
*editor_page
,
9977 WebKitDOMDocument
*document
;
9978 WebKitDOMDocumentFragment
*fragment
= NULL
;
9979 WebKitDOMElement
*element
;
9980 WebKitDOMNode
*node
, *beginning
, *prev_sibling
;
9981 EEditorHistoryEvent
*ev
= NULL
;
9982 gboolean hidden_space
= FALSE
;
9984 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9986 /* We have to be in quoted content. */
9987 if (!e_editor_dom_selection_is_citation (editor_page
))
9990 /* Selection is just caret. */
9991 if (!e_editor_dom_selection_is_collapsed (editor_page
))
9994 document
= e_editor_page_get_document (editor_page
);
9996 e_editor_dom_selection_save (editor_page
);
9998 element
= webkit_dom_document_get_element_by_id (
9999 document
, "-x-evo-selection-start-marker");
10001 /* Before the caret are just quote characters */
10002 beginning
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10003 if (!(beginning
&& WEBKIT_DOM_IS_ELEMENT (beginning
))) {
10004 WebKitDOMNode
*parent
;
10006 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
10007 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
))
10008 beginning
= webkit_dom_node_get_previous_sibling (parent
);
10013 /* Before the text is the beginning of line. */
10014 if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning
), "-x-evo-quoted")))
10017 /* If we are just on the beginning of the line and not on the beginning of
10018 * the block we need to remove the last character ourselves as well, otherwise
10019 * WebKit will put the caret to wrong position. */
10020 if (!(prev_sibling
= webkit_dom_node_get_previous_sibling (beginning
)))
10023 if (key_code
!= ~0) {
10024 ev
= g_new0 (EEditorHistoryEvent
, 1);
10025 ev
->type
= HISTORY_DELETE
;
10027 e_editor_dom_selection_get_coordinates (editor_page
,
10028 &ev
->before
.start
.x
,
10029 &ev
->before
.start
.y
,
10031 &ev
->before
.end
.y
);
10033 fragment
= webkit_dom_document_create_document_fragment (document
);
10036 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
)) {
10037 if (key_code
!= ~0)
10038 webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment
), prev_sibling
, NULL
);
10040 remove_node (prev_sibling
);
10043 prev_sibling
= webkit_dom_node_get_previous_sibling (beginning
);
10044 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
) &&
10045 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (prev_sibling
), "data-hidden-space")) {
10046 hidden_space
= TRUE
;
10047 if (key_code
!= ~0)
10048 webkit_dom_node_insert_before (
10049 WEBKIT_DOM_NODE (fragment
),
10051 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
10054 remove_node (prev_sibling
);
10057 node
= webkit_dom_node_get_previous_sibling (beginning
);
10059 if (key_code
!= ~0)
10060 webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment
), beginning
, NULL
);
10062 remove_node (beginning
);
10064 if (!hidden_space
) {
10065 if (key_code
!= ~0) {
10068 data
= webkit_dom_character_data_substring_data (
10069 WEBKIT_DOM_CHARACTER_DATA (node
),
10070 webkit_dom_character_data_get_length (
10071 WEBKIT_DOM_CHARACTER_DATA (node
)) -1,
10075 webkit_dom_node_append_child (
10076 WEBKIT_DOM_NODE (fragment
),
10078 webkit_dom_document_create_text_node (document
, data
)),
10084 webkit_dom_character_data_delete_data (
10085 WEBKIT_DOM_CHARACTER_DATA (node
),
10086 webkit_dom_character_data_get_length (
10087 WEBKIT_DOM_CHARACTER_DATA (node
)) -1,
10092 if (key_code
!= ~0) {
10093 EEditorUndoRedoManager
*manager
;
10095 e_editor_dom_selection_get_coordinates (editor_page
,
10096 &ev
->after
.start
.x
,
10097 &ev
->after
.start
.y
,
10101 ev
->data
.fragment
= g_object_ref (fragment
);
10103 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10104 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10107 e_editor_dom_selection_restore (editor_page
);
10111 e_editor_dom_selection_restore (editor_page
);
10117 e_editor_dom_delete_last_character_on_line_in_quoted_block (EEditorPage
*editor_page
,
10119 gboolean control_key
)
10121 WebKitDOMDocument
*document
;
10122 WebKitDOMElement
*element
;
10123 WebKitDOMNode
*node
, *beginning
, *next_sibling
;
10124 gboolean success
= FALSE
;
10126 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10128 document
= e_editor_page_get_document (editor_page
);
10130 /* We have to be in quoted content. */
10131 if (!e_editor_dom_selection_is_citation (editor_page
))
10134 /* Selection is just caret. */
10135 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10138 e_editor_dom_selection_save (editor_page
);
10140 element
= webkit_dom_document_get_element_by_id (
10141 document
, "-x-evo-selection-start-marker");
10143 /* selection end marker */
10144 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
10146 /* We have to be on the end of line. */
10147 next_sibling
= webkit_dom_node_get_next_sibling (node
);
10148 if (next_sibling
&&
10149 (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
) ||
10150 webkit_dom_node_get_next_sibling (next_sibling
)))
10153 /* Before the caret is just text. */
10154 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10155 if (!(node
&& WEBKIT_DOM_IS_TEXT (node
)))
10158 /* There is just one character. */
10159 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
)) != 1)
10162 beginning
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (node
));
10163 if (!(beginning
&& WEBKIT_DOM_IS_ELEMENT (beginning
)))
10166 /* Before the text is the beginning of line. */
10167 if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning
), "-x-evo-quoted")))
10170 if (!webkit_dom_node_get_previous_sibling (beginning
))
10173 if (key_code
!= ~0) {
10174 e_editor_dom_selection_restore (editor_page
);
10175 save_history_for_delete_or_backspace (
10176 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10177 e_editor_dom_selection_save (editor_page
);
10180 element
= webkit_dom_node_get_parent_element (beginning
);
10181 remove_node (WEBKIT_DOM_NODE (element
));
10185 e_editor_dom_selection_restore (editor_page
);
10188 e_editor_dom_insert_new_line_into_citation (editor_page
, NULL
);
10194 selection_is_in_empty_list_item (WebKitDOMNode
*selection_start_marker
)
10197 WebKitDOMNode
*sibling
;
10199 /* Selection needs to be collapsed. */
10200 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
10201 if (!e_editor_dom_is_selection_position_node (sibling
))
10204 /* After the selection end there could be just the BR element. */
10205 sibling
= webkit_dom_node_get_next_sibling (sibling
);
10206 if (sibling
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
))
10209 if (sibling
&& webkit_dom_node_get_next_sibling (sibling
))
10212 sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
10217 /* Only text node with the zero width space character is allowed. */
10218 if (!WEBKIT_DOM_IS_TEXT (sibling
))
10221 if (webkit_dom_node_get_previous_sibling (sibling
))
10224 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (sibling
)) != 1)
10227 text
= webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (sibling
));
10228 if (!(text
&& g_strcmp0 (text
, UNICODE_ZERO_WIDTH_SPACE
) == 0)) {
10239 return_pressed_in_image_wrapper (EEditorPage
*editor_page
)
10241 WebKitDOMDocument
*document
;
10242 WebKitDOMDocumentFragment
*fragment
;
10243 WebKitDOMElement
*selection_start_marker
;
10244 WebKitDOMNode
*parent
, *block
, *clone
;
10245 EEditorHistoryEvent
*ev
= NULL
;
10246 EEditorUndoRedoManager
*manager
;
10248 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10250 document
= e_editor_page_get_document (editor_page
);
10252 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10255 e_editor_dom_selection_save (editor_page
);
10257 selection_start_marker
= webkit_dom_document_get_element_by_id (
10258 document
, "-x-evo-selection-start-marker");
10260 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
10261 if (!element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-resizable-wrapper")) {
10262 e_editor_dom_selection_restore (editor_page
);
10266 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10268 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10269 ev
= g_new0 (EEditorHistoryEvent
, 1);
10270 ev
->type
= HISTORY_INPUT
;
10272 e_editor_dom_selection_get_coordinates (editor_page
,
10273 &ev
->before
.start
.x
,
10274 &ev
->before
.start
.y
,
10276 &ev
->before
.end
.y
);
10278 fragment
= webkit_dom_document_create_document_fragment (document
);
10280 g_object_set_data (
10281 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10284 block
= e_editor_dom_get_parent_block_node_from_child (
10285 WEBKIT_DOM_NODE (selection_start_marker
));
10287 clone
= webkit_dom_node_clone_node_with_error (block
, FALSE
, NULL
);
10288 webkit_dom_node_append_child (
10289 clone
, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)), NULL
);
10291 webkit_dom_node_insert_before (
10292 webkit_dom_node_get_parent_node (block
),
10298 webkit_dom_node_append_child (
10299 WEBKIT_DOM_NODE (fragment
),
10300 webkit_dom_node_clone_node_with_error (clone
, TRUE
, NULL
),
10303 e_editor_dom_selection_get_coordinates (editor_page
,
10304 &ev
->after
.start
.x
,
10305 &ev
->after
.start
.y
,
10309 ev
->data
.fragment
= g_object_ref (fragment
);
10311 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10314 e_editor_page_emit_content_changed (editor_page
);
10316 e_editor_dom_selection_restore (editor_page
);
10322 return_pressed_after_h_rule (EEditorPage
*editor_page
)
10324 WebKitDOMDocument
*document
;
10325 WebKitDOMDocumentFragment
*fragment
;
10326 WebKitDOMElement
*selection_marker
;
10327 WebKitDOMNode
*node
, *block
, *clone
, *hr
, *insert_before
= NULL
;
10328 EEditorHistoryEvent
*ev
= NULL
;
10329 EEditorUndoRedoManager
*manager
;
10331 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10333 document
= e_editor_page_get_document (editor_page
);
10335 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10338 e_editor_dom_selection_save (editor_page
);
10340 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10342 if (e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10343 selection_marker
= webkit_dom_document_get_element_by_id (
10344 document
, "-x-evo-selection-end-marker");
10346 hr
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker
));
10347 hr
= webkit_dom_node_get_next_sibling (hr
);
10348 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker
));
10349 if (!node
|| !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
) || !hr
||
10350 !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr
)) {
10351 e_editor_dom_selection_restore (editor_page
);
10355 insert_before
= webkit_dom_node_get_next_sibling (hr
);
10357 selection_marker
= webkit_dom_document_get_element_by_id (
10358 document
, "-x-evo-selection-start-marker");
10360 node
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker
));
10361 hr
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker
));
10362 if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
) ||
10363 !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr
)) {
10364 e_editor_dom_selection_restore (editor_page
);
10368 insert_before
= WEBKIT_DOM_NODE (selection_marker
);
10370 ev
= g_new0 (EEditorHistoryEvent
, 1);
10371 ev
->type
= HISTORY_INPUT
;
10373 e_editor_dom_selection_get_coordinates (editor_page
,
10374 &ev
->before
.start
.x
,
10375 &ev
->before
.start
.y
,
10377 &ev
->before
.end
.y
);
10379 fragment
= webkit_dom_document_create_document_fragment (document
);
10381 g_object_set_data (
10382 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10385 block
= webkit_dom_node_get_previous_sibling (hr
);
10387 clone
= webkit_dom_node_clone_node_with_error (block
, FALSE
, NULL
);
10389 webkit_dom_node_append_child (
10390 clone
, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)), NULL
);
10392 webkit_dom_node_insert_before (
10393 webkit_dom_node_get_parent_node (hr
), clone
, insert_before
, NULL
);
10395 dom_remove_selection_markers (document
);
10397 webkit_dom_node_append_child (
10398 WEBKIT_DOM_NODE (clone
),
10400 dom_create_selection_marker (document
, TRUE
)),
10402 webkit_dom_node_append_child (
10403 WEBKIT_DOM_NODE (clone
),
10405 dom_create_selection_marker (document
, FALSE
)),
10409 webkit_dom_node_append_child (
10410 WEBKIT_DOM_NODE (fragment
),
10411 webkit_dom_node_clone_node_with_error (clone
, TRUE
, NULL
),
10414 e_editor_dom_selection_get_coordinates (editor_page
,
10415 &ev
->after
.start
.x
,
10416 &ev
->after
.start
.y
,
10420 ev
->data
.fragment
= g_object_ref (fragment
);
10422 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10425 e_editor_page_emit_content_changed (editor_page
);
10427 e_editor_dom_selection_restore (editor_page
);
10433 e_editor_dom_return_pressed_in_empty_list_item (EEditorPage
*editor_page
)
10435 WebKitDOMDocument
*document
;
10436 WebKitDOMElement
*selection_start_marker
;
10437 WebKitDOMNode
*parent
;
10439 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10441 document
= e_editor_page_get_document (editor_page
);
10443 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10446 e_editor_dom_selection_save (editor_page
);
10448 selection_start_marker
= webkit_dom_document_get_element_by_id (
10449 document
, "-x-evo-selection-start-marker");
10451 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
10452 if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent
)) {
10453 e_editor_dom_selection_restore (editor_page
);
10457 if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start_marker
))) {
10458 EEditorHistoryEvent
*ev
= NULL
;
10459 EEditorUndoRedoManager
*manager
;
10460 WebKitDOMDocumentFragment
*fragment
;
10461 WebKitDOMElement
*paragraph
;
10462 WebKitDOMNode
*list
;
10464 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10466 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10467 ev
= g_new0 (EEditorHistoryEvent
, 1);
10468 ev
->type
= HISTORY_INPUT
;
10470 e_editor_dom_selection_get_coordinates (editor_page
,
10471 &ev
->before
.start
.x
,
10472 &ev
->before
.start
.y
,
10474 &ev
->before
.end
.y
);
10476 fragment
= webkit_dom_document_create_document_fragment (document
);
10478 g_object_set_data (
10479 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10482 list
= split_list_into_two (parent
, -1);
10485 webkit_dom_node_append_child (
10486 WEBKIT_DOM_NODE (fragment
),
10490 remove_node (parent
);
10493 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
10495 webkit_dom_node_insert_before (
10496 webkit_dom_node_get_parent_node (list
),
10497 WEBKIT_DOM_NODE (paragraph
),
10501 remove_node_if_empty (list
);
10504 e_editor_dom_selection_get_coordinates (editor_page
,
10505 &ev
->after
.start
.x
,
10506 &ev
->after
.start
.y
,
10510 ev
->data
.fragment
= g_object_ref (fragment
);
10512 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10515 e_editor_dom_selection_restore (editor_page
);
10517 e_editor_page_emit_content_changed (editor_page
);
10522 e_editor_dom_selection_restore (editor_page
);
10528 process_smiley_on_delete_or_backspace (EEditorPage
*editor_page
)
10530 WebKitDOMDocument
*document
;
10531 WebKitDOMElement
*element
;
10532 WebKitDOMNode
*parent
;
10533 gboolean in_smiley
= FALSE
;
10535 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
10537 document
= e_editor_page_get_document (editor_page
);
10538 e_editor_dom_selection_save (editor_page
);
10539 element
= webkit_dom_document_get_element_by_id (
10540 document
, "-x-evo-selection-start-marker");
10542 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
10543 if (WEBKIT_DOM_IS_ELEMENT (parent
) &&
10544 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-smiley-text"))
10547 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
10548 WebKitDOMNode
*prev_sibling
;
10550 prev_sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10551 if (prev_sibling
&& WEBKIT_DOM_IS_TEXT (prev_sibling
)) {
10552 gchar
*text
= webkit_dom_character_data_get_data (
10553 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
10555 if (g_strcmp0 (text
, UNICODE_ZERO_WIDTH_SPACE
) == 0) {
10556 WebKitDOMNode
*prev_prev_sibling
;
10558 prev_prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
10559 if (WEBKIT_DOM_IS_ELEMENT (prev_prev_sibling
) &&
10560 element_has_class (WEBKIT_DOM_ELEMENT (prev_prev_sibling
), "-x-evo-smiley-wrapper")) {
10561 remove_node (prev_sibling
);
10563 parent
= webkit_dom_node_get_last_child (prev_prev_sibling
);
10570 element
= webkit_dom_document_get_element_by_id (
10571 document
, "-x-evo-selection-end-marker");
10573 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
10574 if (WEBKIT_DOM_IS_ELEMENT (parent
) &&
10575 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-smiley-text"))
10581 WebKitDOMNode
*wrapper
;
10583 wrapper
= webkit_dom_node_get_parent_node (parent
);
10584 if (!e_editor_page_get_html_mode (editor_page
)) {
10585 WebKitDOMNode
*child
;
10587 while ((child
= webkit_dom_node_get_first_child (parent
)))
10588 webkit_dom_node_insert_before (
10589 webkit_dom_node_get_parent_node (wrapper
),
10594 /* In the HTML mode the whole smiley will be removed. */
10595 remove_node (wrapper
);
10596 /* FIXME history will be probably broken here */
10599 e_editor_dom_selection_restore (editor_page
);
10603 e_editor_dom_key_press_event_process_return_key (EEditorPage
*editor_page
)
10605 WebKitDOMDocument
*document
;
10606 WebKitDOMNode
*table
= NULL
;
10607 gboolean first_cell
= FALSE
;
10609 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10611 document
= e_editor_page_get_document (editor_page
);
10612 /* Return pressed in the beginning of the first cell will insert
10613 * new block before the table (and move the caret there) if none
10614 * is already there, otherwise it will act as normal return. */
10615 if (selection_is_in_table (document
, &first_cell
, &table
) && first_cell
) {
10616 WebKitDOMNode
*node
;
10618 node
= webkit_dom_node_get_previous_sibling (table
);
10620 node
= webkit_dom_node_get_next_sibling (table
);
10621 node
= webkit_dom_node_clone_node_with_error (node
, FALSE
, NULL
);
10622 webkit_dom_node_append_child (
10624 WEBKIT_DOM_NODE (webkit_dom_document_create_element (
10625 document
, "br", NULL
)),
10627 dom_add_selection_markers_into_element_start (
10628 document
, WEBKIT_DOM_ELEMENT (node
), NULL
, NULL
);
10629 webkit_dom_node_insert_before (
10630 webkit_dom_node_get_parent_node (table
),
10634 e_editor_dom_selection_restore (editor_page
);
10635 e_editor_page_emit_content_changed (editor_page
);
10640 /* When user presses ENTER in a citation block, WebKit does
10641 * not break the citation automatically, so we need to use
10642 * the special command to do it. */
10643 if (e_editor_dom_selection_is_citation (editor_page
)) {
10644 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
10645 if (split_citation (editor_page
)) {
10646 e_editor_page_set_return_key_pressed (editor_page
, TRUE
);
10647 e_editor_dom_check_magic_links (editor_page
, FALSE
);
10648 e_editor_page_set_return_key_pressed (editor_page
, FALSE
);
10649 e_editor_page_emit_content_changed (editor_page
);
10656 /* If the ENTER key is pressed inside an empty list item then the list
10657 * is broken into two and empty paragraph is inserted between lists. */
10658 if (e_editor_dom_return_pressed_in_empty_list_item (editor_page
))
10661 if (return_pressed_in_image_wrapper (editor_page
))
10664 if (return_pressed_after_h_rule (editor_page
))
10671 remove_empty_bulleted_list_item (EEditorPage
*editor_page
)
10673 WebKitDOMDocument
*document
;
10674 WebKitDOMElement
*selection_start
;
10675 WebKitDOMNode
*parent
;
10676 EEditorUndoRedoManager
*manager
;
10678 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10680 document
= e_editor_page_get_document (editor_page
);
10681 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10682 e_editor_dom_selection_save (editor_page
);
10684 selection_start
= webkit_dom_document_get_element_by_id (
10685 document
, "-x-evo-selection-start-marker");
10687 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start
));
10688 while (parent
&& !node_is_list_or_item (parent
))
10689 parent
= webkit_dom_node_get_parent_node (parent
);
10694 if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start
))) {
10695 EEditorHistoryEvent
*ev
= NULL
;
10696 WebKitDOMDocumentFragment
*fragment
;
10697 WebKitDOMNode
*prev_item
;
10699 prev_item
= webkit_dom_node_get_previous_sibling (parent
);
10701 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10702 /* Insert new history event for Return to have the right coordinates.
10703 * The fragment will be added later. */
10704 ev
= g_new0 (EEditorHistoryEvent
, 1);
10705 ev
->type
= HISTORY_DELETE
;
10707 e_editor_dom_selection_get_coordinates (editor_page
,
10708 &ev
->before
.start
.x
,
10709 &ev
->before
.start
.y
,
10711 &ev
->before
.end
.y
);
10713 fragment
= webkit_dom_document_create_document_fragment (document
);
10718 webkit_dom_node_append_child (
10719 WEBKIT_DOM_NODE (fragment
),
10720 webkit_dom_node_clone_node_with_error (prev_item
, TRUE
, NULL
),
10723 webkit_dom_node_append_child (
10724 WEBKIT_DOM_NODE (fragment
),
10728 remove_node (parent
);
10731 dom_add_selection_markers_into_element_end (
10732 document
, WEBKIT_DOM_ELEMENT (prev_item
), NULL
, NULL
);
10735 e_editor_dom_selection_get_coordinates (editor_page
,
10736 &ev
->after
.start
.x
,
10737 &ev
->after
.start
.y
,
10741 ev
->data
.fragment
= g_object_ref (fragment
);
10743 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10746 e_editor_page_emit_content_changed (editor_page
);
10747 e_editor_dom_selection_restore (editor_page
);
10752 e_editor_dom_selection_restore (editor_page
);
10758 e_editor_dom_key_press_event_process_backspace_key (EEditorPage
*editor_page
)
10760 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10762 /* BackSpace pressed in the beginning of quoted content changes
10763 * format to normal and inserts text into body */
10764 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
10765 e_editor_dom_selection_save (editor_page
);
10766 if (e_editor_dom_move_quoted_block_level_up (editor_page
) || delete_hidden_space (editor_page
)) {
10767 e_editor_dom_selection_restore (editor_page
);
10768 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
10769 e_editor_page_emit_content_changed (editor_page
);
10772 e_editor_dom_selection_restore (editor_page
);
10775 /* BackSpace in indented block decrease indent level by one */
10776 if (e_editor_dom_selection_is_indented (editor_page
) &&
10777 e_editor_dom_selection_is_collapsed (editor_page
)) {
10778 WebKitDOMDocument
*document
;
10779 WebKitDOMElement
*selection_start
;
10780 WebKitDOMNode
*prev_sibling
;
10782 document
= e_editor_page_get_document (editor_page
);
10784 e_editor_dom_selection_save (editor_page
);
10785 selection_start
= webkit_dom_document_get_element_by_id (
10786 document
, "-x-evo-selection-start-marker");
10788 /* Empty text node before caret */
10789 prev_sibling
= webkit_dom_node_get_previous_sibling (
10790 WEBKIT_DOM_NODE (selection_start
));
10791 if (prev_sibling
&& WEBKIT_DOM_IS_TEXT (prev_sibling
))
10792 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (prev_sibling
)) == 0)
10793 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
10795 e_editor_dom_selection_restore (editor_page
);
10796 if (!prev_sibling
) {
10797 e_editor_dom_selection_unindent (editor_page
);
10798 e_editor_page_emit_content_changed (editor_page
);
10803 /* BackSpace pressed in an empty item in the bulleted list removes it. */
10804 if (!e_editor_page_get_html_mode (editor_page
) && e_editor_dom_selection_is_collapsed (editor_page
) &&
10805 remove_empty_bulleted_list_item (editor_page
))
10809 if (prevent_from_deleting_last_element_in_body (e_editor_page_get_document (editor_page
)))
10816 deleting_block_starting_in_quoted_content (EEditorPage
*editor_page
,
10818 gboolean control_key
)
10820 gint citation_level
;
10821 WebKitDOMDocument
*document
;
10822 WebKitDOMElement
*element
;
10823 WebKitDOMHTMLElement
*body
;
10824 WebKitDOMNode
*node
, *parent
, *block
, *sibling
;
10825 WebKitDOMRange
*range
= NULL
;
10827 e_editor_dom_selection_save (editor_page
);
10829 document
= e_editor_page_get_document (editor_page
);
10831 element
= webkit_dom_document_get_element_by_id (
10832 document
, "-x-evo-selection-start-marker");
10834 /* Before the caret are just quote characters */
10835 sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10836 if (!(sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
10837 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted"))) {
10841 if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (sibling
))) {
10845 element
= webkit_dom_document_get_element_by_id (
10846 document
, "-x-evo-selection-end-marker");
10848 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
10850 enable_quote_marks_select (document
);
10851 e_editor_dom_selection_restore (editor_page
);
10853 if (key_code
!= ~0)
10854 save_history_for_delete_or_backspace (
10855 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10857 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
10859 range
= e_editor_dom_get_current_range (editor_page
);
10860 node
= webkit_dom_range_get_end_container (range
, NULL
);
10862 block
= e_editor_dom_get_parent_block_node_from_child (node
);
10863 parent
= webkit_dom_node_get_parent_node (block
);
10864 body
= webkit_dom_document_get_body (document
);
10865 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent
)))
10866 parent
= webkit_dom_node_get_parent_node (parent
);
10868 if (!citation_level
) {
10869 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
10870 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
10872 webkit_dom_node_insert_before (
10873 WEBKIT_DOM_NODE (body
),
10875 webkit_dom_node_get_next_sibling (parent
),
10878 WebKitDOMNode
*last_child
= webkit_dom_node_get_last_child (block
);
10880 if (WEBKIT_DOM_IS_ELEMENT (last_child
) &&
10881 element_has_class (WEBKIT_DOM_ELEMENT (last_child
), "-x-evo-quoted")) {
10882 webkit_dom_node_append_child (
10884 WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)),
10890 e_editor_dom_disable_quote_marks_select (editor_page
);
10891 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (block
), TRUE
);
10893 g_clear_object (&range
);
10897 e_editor_dom_selection_restore (editor_page
);
10903 e_editor_dom_key_press_event_process_delete_or_backspace_key (EEditorPage
*editor_page
,
10905 gboolean control_key
,
10908 WebKitDOMDocument
*document
;
10909 gboolean html_mode
, local_delete
, collapsed
;
10911 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10913 document
= e_editor_page_get_document (editor_page
);
10914 html_mode
= e_editor_page_get_html_mode (editor_page
);
10915 local_delete
= (key_code
== HTML_KEY_CODE_DELETE
) || delete;
10917 if (e_editor_page_get_magic_smileys_enabled (editor_page
)) {
10918 /* If deleting something in a smiley it won't be a smiley
10919 * anymore (at least from Evolution' POV), so remove all
10920 * the elements that are hidden in the wrapper and leave
10921 * just the text. Also this ensures that when a smiley is
10922 * recognized and we press the BackSpace key we won't delete
10923 * the UNICODE_HIDDEN_SPACE, but we will correctly delete
10924 * the last character of smiley. */
10925 process_smiley_on_delete_or_backspace (editor_page
);
10928 if (!local_delete
&& !html_mode
&&
10929 e_editor_dom_delete_last_character_on_line_in_quoted_block (editor_page
, key_code
, control_key
))
10932 if (!local_delete
&& !html_mode
&&
10933 delete_last_character_from_previous_line_in_quoted_block (editor_page
, key_code
, control_key
))
10936 if (!html_mode
&& e_editor_dom_fix_structure_after_delete_before_quoted_content (editor_page
, key_code
, control_key
, delete))
10939 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
10941 if (!html_mode
&& !collapsed
&& deleting_block_starting_in_quoted_content (editor_page
, key_code
, control_key
))
10945 /* Let the quote marks be selectable to nearly correctly remove the
10946 * selection. Corrections after are done in body_keyup_event_cb. */
10947 enable_quote_marks_select (document
);
10950 if (key_code
!= ~0)
10951 save_history_for_delete_or_backspace (
10952 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10954 if (local_delete
) {
10955 WebKitDOMElement
*selection_start_marker
;
10956 WebKitDOMNode
*sibling
, *block
, *next_block
;
10958 /* This needs to be performed just in plain text mode
10959 * and when the selection is collapsed. */
10963 e_editor_dom_selection_save (editor_page
);
10965 selection_start_marker
= webkit_dom_document_get_element_by_id (
10966 document
, "-x-evo-selection-start-marker");
10967 sibling
= webkit_dom_node_get_previous_sibling (
10968 WEBKIT_DOM_NODE (selection_start_marker
));
10969 /* Check if the key was pressed in the beginning of block. */
10970 if (!(sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
10971 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted"))) {
10972 e_editor_dom_selection_restore (editor_page
);
10976 sibling
= webkit_dom_node_get_next_sibling (
10977 WEBKIT_DOM_NODE (selection_start_marker
));
10978 sibling
= webkit_dom_node_get_next_sibling (sibling
);
10980 /* And also the current block was empty. */
10981 if (!(!sibling
|| (sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
10982 !element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-wrap-br")))) {
10983 e_editor_dom_selection_restore (editor_page
);
10987 block
= e_editor_dom_get_parent_block_node_from_child (
10988 WEBKIT_DOM_NODE (selection_start_marker
));
10989 next_block
= webkit_dom_node_get_next_sibling (block
);
10991 remove_node (block
);
10993 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (next_block
), TRUE
);
10997 /* Concatenating a non-quoted block with Backspace key to the
10998 * previous block that is inside a quoted content. */
10999 WebKitDOMElement
*selection_start_marker
;
11000 WebKitDOMNode
*node
, *block
, *prev_block
, *last_child
, *child
;
11002 if (html_mode
|| e_editor_dom_selection_is_citation (editor_page
))
11005 e_editor_dom_selection_save (editor_page
);
11007 selection_start_marker
= webkit_dom_document_get_element_by_id (
11008 document
, "-x-evo-selection-start-marker");
11010 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
11012 e_editor_dom_selection_restore (editor_page
);
11016 remove_empty_blocks (document
);
11018 block
= e_editor_dom_get_parent_block_node_from_child (
11019 WEBKIT_DOM_NODE (selection_start_marker
));
11021 prev_block
= webkit_dom_node_get_previous_sibling (block
);
11022 if (!prev_block
|| !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block
)) {
11023 e_editor_dom_selection_restore (editor_page
);
11027 last_child
= webkit_dom_node_get_last_child (prev_block
);
11028 while (last_child
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child
))
11029 last_child
= webkit_dom_node_get_last_child (last_child
);
11032 e_editor_dom_selection_restore (editor_page
);
11036 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child
));
11037 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child
));
11039 node
= webkit_dom_node_get_last_child (last_child
);
11040 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
))
11041 remove_node (node
);
11043 while ((child
= webkit_dom_node_get_first_child (block
)))
11044 webkit_dom_node_append_child (last_child
, child
, NULL
);
11046 remove_node (block
);
11048 if (WEBKIT_DOM_IS_ELEMENT (last_child
))
11049 e_editor_dom_wrap_and_quote_element (editor_page
, WEBKIT_DOM_ELEMENT (last_child
));
11051 e_editor_dom_selection_restore (editor_page
);
11058 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
11059 e_editor_page_emit_content_changed (editor_page
);
11065 contains_forbidden_elements (WebKitDOMDocument
*document
)
11067 WebKitDOMElement
*body
, *element
;
11069 body
= WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document
));
11071 /* Try to find disallowed elements in the plain text mode */
11072 element
= webkit_dom_element_query_selector (
11075 /* Basic elements used as blocks allowed in the plain text mode */
11076 "[data-evo-paragraph], pre, ul, ol, li, blockquote[type=cite], "
11077 /* Other elements */
11079 /* Indented elements */
11080 ".-x-evo-indented, "
11082 ".-x-evo-signature-wrapper, .-x-evo-signature, "
11084 ".-x-evo-smiley-wrapper, .-x-evo-smiley-img, .-x-evo-smiley-text, "
11085 /* Selection markers */
11086 "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker"
11093 /* Try to find disallowed elements relationship in the plain text */
11094 element
= webkit_dom_element_query_selector (
11097 /* Body descendants */
11098 "body > :matches(blockquote[type=cite], .-x-evo-signature-wrapper), "
11099 /* Main blocks and indented blocks */
11100 ":matches(body, .-x-evo-indented) > :matches(pre, ul, ol, .-x-evo-indented, [data-evo-paragraph]), "
11101 /* Blockquote descendants */
11102 "blockquote[type=cite] > :matches(pre, [data-evo-paragraph], blockquote[type=cite]), "
11103 /* Block descendants */
11104 ":matches(pre, [data-evo-paragraph], li) > :matches(br, span, a), "
11106 ":matches(ul, ol) > :matches(ul, ol, li), "
11108 ".-x-evo-smiley-wrapper > :matches(.-x-evo-smiley-img, .-x-evo-smiley-text), "
11110 ".-x-evo-signature-wrapper > .-x-evo-signature"
11114 return element
? TRUE
: FALSE
;
11118 e_editor_dom_check_if_conversion_needed (EEditorPage
*editor_page
)
11120 WebKitDOMDocument
*document
;
11121 gboolean html_mode
, convert
= FALSE
;
11123 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
11125 document
= e_editor_page_get_document (editor_page
);
11126 html_mode
= e_editor_page_get_html_mode (editor_page
);
11129 convert
= contains_forbidden_elements (document
);
11135 e_editor_dom_process_content_after_mode_change (EEditorPage
*editor_page
)
11137 EEditorUndoRedoManager
*manager
;
11138 gboolean html_mode
;
11140 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11142 html_mode
= e_editor_page_get_html_mode (editor_page
);
11145 process_content_to_html_changing_composer_mode (editor_page
);
11147 process_content_to_plain_text_changing_composer_mode (editor_page
);
11149 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11150 e_editor_undo_redo_manager_clean_history (manager
);
11154 e_editor_dom_get_caret_offset (EEditorPage
*editor_page
)
11156 WebKitDOMDocument
*document
;
11157 WebKitDOMDOMWindow
*dom_window
= NULL
;
11158 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11159 WebKitDOMNode
*anchor
;
11160 WebKitDOMRange
*range
= NULL
;
11164 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
11166 document
= e_editor_page_get_document (editor_page
);
11167 dom_window
= webkit_dom_document_get_default_view (document
);
11168 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11169 g_clear_object (&dom_window
);
11171 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
11172 g_clear_object (&dom_selection
);
11176 webkit_dom_dom_selection_collapse_to_start (dom_selection
, NULL
);
11177 /* Select the text from the current caret position to the beginning of the line. */
11178 webkit_dom_dom_selection_modify (dom_selection
, "extend", "left", "lineBoundary");
11180 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11181 anchor
= webkit_dom_dom_selection_get_anchor_node (dom_selection
);
11182 text
= webkit_dom_range_to_string (range
, NULL
);
11183 ret_val
= strlen (text
);
11186 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
11188 /* In the plain text mode we need to increase the return value by 2 per
11189 * citation level because of "> ". */
11190 if (!e_editor_page_get_html_mode (editor_page
)) {
11191 WebKitDOMNode
*parent
= anchor
;
11193 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
11194 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent
))
11197 parent
= webkit_dom_node_get_parent_node (parent
);
11201 g_clear_object (&range
);
11202 g_clear_object (&dom_selection
);
11208 e_editor_dom_get_caret_position (EEditorPage
*editor_page
)
11210 WebKitDOMDocument
*document
;
11211 WebKitDOMHTMLElement
*body
;
11212 WebKitDOMDOMWindow
*dom_window
= NULL
;
11213 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11214 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
11218 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
11220 document
= e_editor_page_get_document (editor_page
);
11221 dom_window
= webkit_dom_document_get_default_view (document
);
11222 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11223 g_clear_object (&dom_window
);
11225 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
11226 g_clear_object (&dom_selection
);
11230 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11231 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
11233 body
= webkit_dom_document_get_body (document
);
11234 /* Select the text from the beginning of the body to the current caret. */
11235 webkit_dom_range_set_start_before (
11236 range_clone
, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)), NULL
);
11238 /* This is returning a text without new lines! */
11239 text
= webkit_dom_range_to_string (range_clone
, NULL
);
11240 ret_val
= strlen (text
);
11243 g_clear_object (&range_clone
);
11244 g_clear_object (&range
);
11245 g_clear_object (&dom_selection
);
11251 insert_nbsp_history_event (WebKitDOMDocument
*document
,
11252 EEditorUndoRedoManager
*manager
,
11257 EEditorHistoryEvent
*event
;
11258 WebKitDOMDocumentFragment
*fragment
;
11260 event
= g_new0 (EEditorHistoryEvent
, 1);
11261 event
->type
= HISTORY_AND
;
11262 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11264 fragment
= webkit_dom_document_create_document_fragment (document
);
11265 webkit_dom_node_append_child (
11266 WEBKIT_DOM_NODE (fragment
),
11268 webkit_dom_document_create_text_node (document
, UNICODE_NBSP
)),
11271 event
= g_new0 (EEditorHistoryEvent
, 1);
11272 event
->type
= HISTORY_DELETE
;
11275 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (1));
11277 event
->data
.fragment
= fragment
;
11279 event
->before
.start
.x
= x
;
11280 event
->before
.start
.y
= y
;
11281 event
->before
.end
.x
= x
;
11282 event
->before
.end
.y
= y
;
11284 event
->after
.start
.x
= x
;
11285 event
->after
.start
.y
= y
;
11286 event
->after
.end
.x
= x
;
11287 event
->after
.end
.y
= y
;
11289 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11292 e_editor_dom_save_history_for_drag (EEditorPage
*editor_page
)
11294 WebKitDOMDocument
*document
;
11295 WebKitDOMDocumentFragment
*fragment
;
11296 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11297 WebKitDOMDOMWindow
*dom_window
= NULL
;
11298 WebKitDOMRange
*beginning_of_line
= NULL
;
11299 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
11300 EEditorHistoryEvent
*event
;
11301 EEditorUndoRedoManager
*manager
;
11302 gboolean start_to_start
= FALSE
, end_to_end
= FALSE
;
11306 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11308 document
= e_editor_page_get_document (editor_page
);
11309 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11311 if (!(dom_window
= webkit_dom_document_get_default_view (document
)))
11314 if (!(dom_selection
= webkit_dom_dom_window_get_selection (dom_window
))) {
11315 g_clear_object (&dom_window
);
11319 g_clear_object (&dom_window
);
11321 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
11322 g_clear_object (&dom_selection
);
11326 /* Obtain the dragged content. */
11327 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11328 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
11330 /* Create the history event for the content that will
11331 * be removed by DnD. */
11332 event
= g_new0 (EEditorHistoryEvent
, 1);
11333 event
->type
= HISTORY_DELETE
;
11335 e_editor_dom_selection_get_coordinates (editor_page
,
11336 &event
->before
.start
.x
,
11337 &event
->before
.start
.y
,
11338 &event
->before
.end
.x
,
11339 &event
->before
.end
.y
);
11341 x
= event
->before
.start
.x
;
11342 y
= event
->before
.start
.y
;
11344 event
->after
.start
.x
= x
;
11345 event
->after
.start
.y
= y
;
11346 event
->after
.end
.x
= x
;
11347 event
->after
.end
.y
= y
;
11349 /* Save the content that will be removed. */
11350 fragment
= webkit_dom_range_clone_contents (range_clone
, NULL
);
11352 /* Extend the cloned range to point one character after
11353 * the selection ends to later check if there is a whitespace
11355 webkit_dom_range_set_end (
11357 webkit_dom_range_get_end_container (range_clone
, NULL
),
11358 webkit_dom_range_get_end_offset (range_clone
, NULL
) + 1,
11360 range_text
= webkit_dom_range_get_text (range_clone
);
11362 /* Check if the current selection starts on the beginning of line. */
11363 webkit_dom_dom_selection_modify (
11364 dom_selection
, "extend", "left", "lineboundary");
11365 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11366 start_to_start
= webkit_dom_range_compare_boundary_points (
11367 beginning_of_line
, WEBKIT_DOM_RANGE_START_TO_START
, range
, NULL
) == 0;
11369 /* Restore the selection to state before the check. */
11370 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11371 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11372 g_clear_object (&beginning_of_line
);
11374 /* Check if the current selection end on the end of the line. */
11375 webkit_dom_dom_selection_modify (
11376 dom_selection
, "extend", "right", "lineboundary");
11377 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11378 end_to_end
= webkit_dom_range_compare_boundary_points (
11379 beginning_of_line
, WEBKIT_DOM_RANGE_END_TO_END
, range
, NULL
) == 0;
11381 /* Dragging the whole line. */
11382 if (start_to_start
&& end_to_end
) {
11383 WebKitDOMNode
*container
, *actual_block
, *tmp_block
;
11385 /* Select the whole line (to the beginning of the next
11386 * one so we can reuse the undo code while undoing this.
11387 * Because of this we need to special mark the event
11388 * with history-drag-and-drop to correct the selection
11389 * after undoing it (otherwise the beginning of the next
11390 * line will be selected as well. */
11391 webkit_dom_dom_selection_modify (
11392 dom_selection
, "extend", "right", "character");
11393 g_clear_object (&beginning_of_line
);
11394 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11396 container
= webkit_dom_range_get_end_container (range
, NULL
);
11397 actual_block
= e_editor_dom_get_parent_block_node_from_child (container
);
11399 tmp_block
= webkit_dom_range_get_end_container (beginning_of_line
, NULL
);
11400 if ((tmp_block
= e_editor_dom_get_parent_block_node_from_child (tmp_block
))) {
11401 e_editor_dom_selection_get_coordinates (editor_page
,
11402 &event
->before
.start
.x
,
11403 &event
->before
.start
.y
,
11404 &event
->before
.end
.x
,
11405 &event
->before
.end
.y
);
11407 /* Create the right content for the history event. */
11408 fragment
= webkit_dom_document_create_document_fragment (document
);
11409 /* The removed line. */
11410 webkit_dom_node_append_child (
11411 WEBKIT_DOM_NODE (fragment
),
11412 webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
),
11414 /* The following block, but empty. */
11415 webkit_dom_node_append_child (
11416 WEBKIT_DOM_NODE (fragment
),
11417 webkit_dom_node_clone_node_with_error (tmp_block
, FALSE
, NULL
),
11419 g_object_set_data (
11420 G_OBJECT (fragment
),
11421 "history-drag-and-drop",
11422 GINT_TO_POINTER (1));
11425 /* It should act as a Delete key press. */
11426 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (1));
11428 event
->data
.fragment
= fragment
;
11429 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11431 /* WebKit removes the space (if presented) after selection and
11432 * we need to create a new history event for it. */
11433 if (g_str_has_suffix (range_text
, " ") ||
11434 g_str_has_suffix (range_text
, UNICODE_NBSP
))
11435 insert_nbsp_history_event (document
, manager
, TRUE
, x
, y
);
11437 /* If there is a space before the selection WebKit will remove
11438 * it as well unless there is a space after the selection. */
11439 gchar
*range_text_start
;
11440 glong start_offset
;
11442 start_offset
= webkit_dom_range_get_start_offset (range_clone
, NULL
);
11443 webkit_dom_range_set_start (
11445 webkit_dom_range_get_start_container (range_clone
, NULL
),
11446 start_offset
> 0 ? start_offset
- 1 : 0,
11449 range_text_start
= webkit_dom_range_get_text (range_clone
);
11450 if (g_str_has_prefix (range_text_start
, " ") ||
11451 g_str_has_prefix (range_text_start
, UNICODE_NBSP
)) {
11453 webkit_dom_dom_selection_collapse_to_start (dom_selection
, NULL
);
11454 webkit_dom_dom_selection_modify (
11455 dom_selection
, "move", "backward", "character");
11456 e_editor_dom_selection_get_coordinates (editor_page
, &x
, &y
, &x
, &y
);
11458 insert_nbsp_history_event (document
, manager
, TRUE
, x
, y
);
11461 g_free (range_text_start
);
11464 g_free (range_text
);
11466 /* Restore the selection to original state. */
11467 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11468 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11469 g_clear_object (&beginning_of_line
);
11471 /* All the things above were about removing the content,
11472 * create an AND event to continue later with inserting
11473 * the dropped content. */
11474 event
= g_new0 (EEditorHistoryEvent
, 1);
11475 event
->type
= HISTORY_AND
;
11476 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11478 g_clear_object (&dom_selection
);
11480 g_clear_object (&range
);
11481 g_clear_object (&range_clone
);
11485 e_editor_dom_save_history_for_drop (EEditorPage
*editor_page
)
11487 WebKitDOMDocument
*document
;
11488 WebKitDOMDocumentFragment
*fragment
;
11489 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11490 WebKitDOMDOMWindow
*dom_window
= NULL
;
11491 WebKitDOMNodeList
*list
= NULL
;
11492 WebKitDOMRange
*range
= NULL
;
11493 EEditorUndoRedoManager
*manager
;
11494 EEditorHistoryEvent
*event
;
11497 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11499 document
= e_editor_page_get_document (editor_page
);
11500 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11502 /* When the image is DnD inside the view WebKit removes the wrapper that
11503 * is used for resizing the image, so we have to recreate it again. */
11504 list
= webkit_dom_document_query_selector_all (document
, ":not(span) > img[data-inline]", NULL
);
11505 length
= webkit_dom_node_list_get_length (list
);
11506 for (ii
= 0; ii
< length
; ii
++) {
11507 WebKitDOMElement
*element
;
11508 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
11510 element
= webkit_dom_document_create_element (document
, "span", NULL
);
11511 webkit_dom_element_set_class_name (element
, "-x-evo-resizable-wrapper");
11513 webkit_dom_node_insert_before (
11514 webkit_dom_node_get_parent_node (node
),
11515 WEBKIT_DOM_NODE (element
),
11519 webkit_dom_node_append_child (WEBKIT_DOM_NODE (element
), node
, NULL
);
11521 g_clear_object (&list
);
11523 /* When the image is moved the new selection is created after after it, so
11524 * lets collapse the selection to have the caret right after the image. */
11525 dom_window
= webkit_dom_document_get_default_view (document
);
11526 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11527 g_clear_object (&dom_window
);
11529 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11531 event
= g_new0 (EEditorHistoryEvent
, 1);
11532 event
->type
= HISTORY_INSERT_HTML
;
11534 /* Get the dropped content. It's easy as it is selected by WebKit. */
11535 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
11536 event
->data
.string
.from
= NULL
;
11537 /* Get the HTML content of the dropped content. */
11538 event
->data
.string
.to
= dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment
));
11540 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11542 g_clear_object (&range
);
11543 g_clear_object (&dom_selection
);
11547 dom_set_link_color_in_document (EEditorPage
*editor_page
,
11548 const gchar
*color
,
11551 WebKitDOMDocument
*document
;
11552 WebKitDOMHTMLHeadElement
*head
;
11553 WebKitDOMElement
*style_element
;
11554 WebKitDOMHTMLElement
*body
;
11555 gchar
*color_str
= NULL
;
11556 const gchar
*style_id
;
11558 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11559 g_return_if_fail (color
!= NULL
);
11561 style_id
= visited
? "-x-evo-a-color-style-visited" : "-x-evo-a-color-style";
11563 document
= e_editor_page_get_document (editor_page
);
11564 head
= webkit_dom_document_get_head (document
);
11565 body
= webkit_dom_document_get_body (document
);
11567 style_element
= webkit_dom_document_get_element_by_id (document
, style_id
);
11568 if (!style_element
) {
11569 style_element
= webkit_dom_document_create_element (document
, "style", NULL
);
11570 webkit_dom_element_set_id (style_element
, style_id
);
11571 webkit_dom_element_set_attribute (style_element
, "type", "text/css", NULL
);
11572 webkit_dom_node_append_child (
11573 WEBKIT_DOM_NODE (head
), WEBKIT_DOM_NODE (style_element
), NULL
);
11576 color_str
= g_strdup_printf (
11577 visited
? "a.-x-evo-visited-link { color: %s; }" : "a { color: %s; }", color
);
11578 webkit_dom_element_set_inner_html (style_element
, color_str
, NULL
);
11579 g_free (color_str
);
11582 webkit_dom_html_body_element_set_v_link (
11583 WEBKIT_DOM_HTML_BODY_ELEMENT (body
), color
);
11585 webkit_dom_html_body_element_set_link (
11586 WEBKIT_DOM_HTML_BODY_ELEMENT (body
), color
);
11590 e_editor_dom_set_link_color (EEditorPage
*editor_page
,
11591 const gchar
*color
)
11593 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11595 dom_set_link_color_in_document (editor_page
, color
, FALSE
);
11599 e_editor_dom_set_visited_link_color (EEditorPage
*editor_page
,
11600 const gchar
*color
)
11602 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11604 dom_set_link_color_in_document (editor_page
, color
, TRUE
);
11608 e_editor_dom_fix_file_uri_images (EEditorPage
*editor_page
)
11610 WebKitDOMDocument
*document
;
11611 WebKitDOMNodeList
*list
= NULL
;
11614 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11616 document
= e_editor_page_get_document (editor_page
);
11618 list
= webkit_dom_document_query_selector_all (
11619 document
, "img[src^=\"file://\"]", NULL
);
11620 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
11621 WebKitDOMNode
*node
;
11624 node
= webkit_dom_node_list_item (list
, ii
);
11625 uri
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "src");
11629 g_clear_object (&list
);
11632 /* ******************** Selection ******************** */
11635 e_editor_dom_replace_base64_image_src (EEditorPage
*editor_page
,
11636 const gchar
*selector
,
11637 const gchar
*base64_content
,
11638 const gchar
*filename
,
11641 WebKitDOMDocument
*document
;
11642 WebKitDOMElement
*element
;
11644 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11646 document
= e_editor_page_get_document (editor_page
);
11647 element
= webkit_dom_document_query_selector (document
, selector
, NULL
);
11649 if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element
))
11650 webkit_dom_html_image_element_set_src (
11651 WEBKIT_DOM_HTML_IMAGE_ELEMENT (element
),
11654 webkit_dom_element_set_attribute (
11655 element
, "background", base64_content
, NULL
);
11657 webkit_dom_element_set_attribute (element
, "data-uri", uri
, NULL
);
11658 webkit_dom_element_set_attribute (element
, "data-inline", "", NULL
);
11659 webkit_dom_element_set_attribute (
11660 element
, "data-name", filename
? filename
: "", NULL
);
11664 e_editor_dom_get_current_range (EEditorPage
*editor_page
)
11666 WebKitDOMDocument
*document
;
11667 WebKitDOMDOMWindow
*dom_window
= NULL
;
11668 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11669 WebKitDOMRange
*range
= NULL
;
11671 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
11673 document
= e_editor_page_get_document (editor_page
);
11674 dom_window
= webkit_dom_document_get_default_view (document
);
11678 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11679 if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection
)) {
11680 g_clear_object (&dom_window
);
11684 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1)
11687 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11689 g_clear_object (&dom_selection
);
11690 g_clear_object (&dom_window
);
11696 e_editor_dom_move_caret_into_element (EEditorPage
*editor_page
,
11697 WebKitDOMElement
*element
,
11700 WebKitDOMDocument
*document
;
11701 WebKitDOMDOMWindow
*dom_window
= NULL
;
11702 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11703 WebKitDOMRange
*range
= NULL
;
11705 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11710 document
= e_editor_page_get_document (editor_page
);
11711 dom_window
= webkit_dom_document_get_default_view (document
);
11712 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11713 range
= webkit_dom_document_create_range (document
);
11715 webkit_dom_range_select_node_contents (
11716 range
, WEBKIT_DOM_NODE (element
), NULL
);
11717 webkit_dom_range_collapse (range
, to_start
, NULL
);
11718 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11719 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11721 g_clear_object (&range
);
11722 g_clear_object (&dom_selection
);
11723 g_clear_object (&dom_window
);
11727 e_editor_dom_insert_base64_image (EEditorPage
*editor_page
,
11728 const gchar
*base64_content
,
11729 const gchar
*filename
,
11732 WebKitDOMDocument
*document
;
11733 WebKitDOMElement
*element
, *selection_start_marker
, *resizable_wrapper
;
11734 WebKitDOMText
*text
;
11735 EEditorHistoryEvent
*ev
= NULL
;
11736 EEditorUndoRedoManager
*manager
;
11738 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11740 document
= e_editor_page_get_document (editor_page
);
11741 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11743 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
11744 EEditorHistoryEvent
*ev
;
11745 WebKitDOMDocumentFragment
*fragment
;
11746 WebKitDOMRange
*range
= NULL
;
11748 ev
= g_new0 (EEditorHistoryEvent
, 1);
11749 ev
->type
= HISTORY_DELETE
;
11751 range
= e_editor_dom_get_current_range (editor_page
);
11752 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
11753 g_clear_object (&range
);
11754 ev
->data
.fragment
= g_object_ref (fragment
);
11756 e_editor_dom_selection_get_coordinates (editor_page
,
11757 &ev
->before
.start
.x
,
11758 &ev
->before
.start
.y
,
11760 &ev
->before
.end
.y
);
11762 ev
->after
.start
.x
= ev
->before
.start
.x
;
11763 ev
->after
.start
.y
= ev
->before
.start
.y
;
11764 ev
->after
.end
.x
= ev
->before
.start
.x
;
11765 ev
->after
.end
.y
= ev
->before
.start
.y
;
11767 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11769 ev
= g_new0 (EEditorHistoryEvent
, 1);
11770 ev
->type
= HISTORY_AND
;
11772 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11773 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
11776 e_editor_dom_selection_save (editor_page
);
11777 selection_start_marker
= webkit_dom_document_get_element_by_id (
11778 document
, "-x-evo-selection-start-marker");
11780 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
11781 ev
= g_new0 (EEditorHistoryEvent
, 1);
11782 ev
->type
= HISTORY_IMAGE
;
11784 e_editor_dom_selection_get_coordinates (editor_page
,
11785 &ev
->before
.start
.x
,
11786 &ev
->before
.start
.y
,
11788 &ev
->before
.end
.y
);
11791 resizable_wrapper
=
11792 webkit_dom_document_create_element (document
, "span", NULL
);
11793 webkit_dom_element_set_attribute (
11794 resizable_wrapper
, "class", "-x-evo-resizable-wrapper", NULL
);
11796 element
= webkit_dom_document_create_element (document
, "img", NULL
);
11797 webkit_dom_html_image_element_set_src (
11798 WEBKIT_DOM_HTML_IMAGE_ELEMENT (element
),
11800 webkit_dom_element_set_attribute (
11801 WEBKIT_DOM_ELEMENT (element
), "data-uri", uri
, NULL
);
11802 webkit_dom_element_set_attribute (
11803 WEBKIT_DOM_ELEMENT (element
), "data-inline", "", NULL
);
11804 webkit_dom_element_set_attribute (
11805 WEBKIT_DOM_ELEMENT (element
), "data-name",
11806 filename
? filename
: "", NULL
);
11807 webkit_dom_node_append_child (
11808 WEBKIT_DOM_NODE (resizable_wrapper
),
11809 WEBKIT_DOM_NODE (element
),
11812 webkit_dom_node_insert_before (
11813 webkit_dom_node_get_parent_node (
11814 WEBKIT_DOM_NODE (selection_start_marker
)),
11815 WEBKIT_DOM_NODE (resizable_wrapper
),
11816 WEBKIT_DOM_NODE (selection_start_marker
),
11819 /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
11820 * caret on right position */
11821 text
= webkit_dom_document_create_text_node (
11822 document
, UNICODE_ZERO_WIDTH_SPACE
);
11824 webkit_dom_node_insert_before (
11825 webkit_dom_node_get_parent_node (
11826 WEBKIT_DOM_NODE (selection_start_marker
)),
11827 WEBKIT_DOM_NODE (text
),
11828 WEBKIT_DOM_NODE (selection_start_marker
),
11832 WebKitDOMDocumentFragment
*fragment
;
11833 WebKitDOMNode
*node
;
11835 fragment
= webkit_dom_document_create_document_fragment (document
);
11836 node
= webkit_dom_node_append_child (
11837 WEBKIT_DOM_NODE (fragment
),
11838 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (resizable_wrapper
), TRUE
, NULL
),
11841 webkit_dom_element_insert_adjacent_html (
11842 WEBKIT_DOM_ELEMENT (node
), "afterend", "​", NULL
);
11843 ev
->data
.fragment
= g_object_ref (fragment
);
11845 e_editor_dom_selection_get_coordinates (editor_page
,
11846 &ev
->after
.start
.x
,
11847 &ev
->after
.start
.y
,
11851 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11854 e_editor_dom_selection_restore (editor_page
);
11855 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
11856 e_editor_dom_scroll_to_caret (editor_page
);
11859 /* ************************ image_load_and_insert_async() ************************ */
11861 typedef struct _ImageLoadContext
{
11862 EEditorPage
*editor_page
;
11863 GInputStream
*input_stream
;
11864 GOutputStream
*output_stream
;
11866 GFileInfo
*file_info
;
11867 goffset total_num_bytes
;
11869 const gchar
*content_type
;
11870 const gchar
*filename
;
11871 const gchar
*selector
;
11872 gchar buffer
[4096];
11873 } ImageLoadContext
;
11875 /* Forward Declaration */
11877 image_load_stream_read_cb (GInputStream
*input_stream
,
11878 GAsyncResult
*result
,
11879 ImageLoadContext
*load_context
);
11881 static ImageLoadContext
*
11882 image_load_context_new (EEditorPage
*editor_page
)
11884 ImageLoadContext
*load_context
;
11886 load_context
= g_slice_new0 (ImageLoadContext
);
11887 load_context
->editor_page
= editor_page
;
11889 return load_context
;
11893 image_load_context_free (ImageLoadContext
*load_context
)
11895 if (load_context
->input_stream
!= NULL
)
11896 g_object_unref (load_context
->input_stream
);
11898 if (load_context
->output_stream
!= NULL
)
11899 g_object_unref (load_context
->output_stream
);
11901 if (load_context
->file_info
!= NULL
)
11902 g_object_unref (load_context
->file_info
);
11904 if (load_context
->file
!= NULL
)
11905 g_object_unref (load_context
->file
);
11907 g_slice_free (ImageLoadContext
, load_context
);
11911 image_load_finish (ImageLoadContext
*load_context
)
11913 EEditorPage
*editor_page
;
11914 GMemoryOutputStream
*output_stream
;
11915 const gchar
*selector
;
11916 gchar
*base64_encoded
, *mime_type
, *output
, *uri
;
11920 output_stream
= G_MEMORY_OUTPUT_STREAM (load_context
->output_stream
);
11921 editor_page
= load_context
->editor_page
;
11922 mime_type
= g_content_type_get_mime_type (load_context
->content_type
);
11924 data
= g_memory_output_stream_get_data (output_stream
);
11925 size
= g_memory_output_stream_get_data_size (output_stream
);
11926 uri
= g_file_get_uri (load_context
->file
);
11928 base64_encoded
= g_base64_encode ((const guchar
*) data
, size
);
11929 output
= g_strconcat ("data:", mime_type
, ";base64,", base64_encoded
, NULL
);
11930 selector
= load_context
->selector
;
11931 if (selector
&& *selector
)
11932 e_editor_dom_replace_base64_image_src (editor_page
, selector
, output
, load_context
->filename
, uri
);
11934 e_editor_dom_insert_base64_image (editor_page
, output
, load_context
->filename
, uri
);
11936 g_free (base64_encoded
);
11938 g_free (mime_type
);
11941 image_load_context_free (load_context
);
11945 image_load_write_cb (GOutputStream
*output_stream
,
11946 GAsyncResult
*result
,
11947 ImageLoadContext
*load_context
)
11949 GInputStream
*input_stream
;
11950 gssize bytes_written
;
11951 GError
*error
= NULL
;
11953 bytes_written
= g_output_stream_write_finish (
11954 output_stream
, result
, &error
);
11957 image_load_context_free (load_context
);
11961 input_stream
= load_context
->input_stream
;
11963 if (bytes_written
< load_context
->bytes_read
) {
11965 load_context
->buffer
,
11966 load_context
->buffer
+ bytes_written
,
11967 load_context
->bytes_read
- bytes_written
);
11968 load_context
->bytes_read
-= bytes_written
;
11970 g_output_stream_write_async (
11972 load_context
->buffer
,
11973 load_context
->bytes_read
,
11974 G_PRIORITY_DEFAULT
, NULL
,
11975 (GAsyncReadyCallback
) image_load_write_cb
,
11978 g_input_stream_read_async (
11980 load_context
->buffer
,
11981 sizeof (load_context
->buffer
),
11982 G_PRIORITY_DEFAULT
, NULL
,
11983 (GAsyncReadyCallback
) image_load_stream_read_cb
,
11988 image_load_stream_read_cb (GInputStream
*input_stream
,
11989 GAsyncResult
*result
,
11990 ImageLoadContext
*load_context
)
11992 GOutputStream
*output_stream
;
11994 GError
*error
= NULL
;
11996 bytes_read
= g_input_stream_read_finish (
11997 input_stream
, result
, &error
);
12000 image_load_context_free (load_context
);
12004 if (bytes_read
== 0) {
12005 image_load_finish (load_context
);
12009 output_stream
= load_context
->output_stream
;
12010 load_context
->bytes_read
= bytes_read
;
12012 g_output_stream_write_async (
12014 load_context
->buffer
,
12015 load_context
->bytes_read
,
12016 G_PRIORITY_DEFAULT
, NULL
,
12017 (GAsyncReadyCallback
) image_load_write_cb
,
12022 image_load_file_read_cb (GFile
*file
,
12023 GAsyncResult
*result
,
12024 ImageLoadContext
*load_context
)
12026 GFileInputStream
*input_stream
;
12027 GOutputStream
*output_stream
;
12028 GError
*error
= NULL
;
12030 /* Input stream might be NULL, so don't use cast macro. */
12031 input_stream
= g_file_read_finish (file
, result
, &error
);
12032 load_context
->input_stream
= (GInputStream
*) input_stream
;
12035 image_load_context_free (load_context
);
12039 /* Load the contents into a GMemoryOutputStream. */
12040 output_stream
= g_memory_output_stream_new (
12041 NULL
, 0, g_realloc
, g_free
);
12043 load_context
->output_stream
= output_stream
;
12045 g_input_stream_read_async (
12046 load_context
->input_stream
,
12047 load_context
->buffer
,
12048 sizeof (load_context
->buffer
),
12049 G_PRIORITY_DEFAULT
, NULL
,
12050 (GAsyncReadyCallback
) image_load_stream_read_cb
,
12055 image_load_query_info_cb (GFile
*file
,
12056 GAsyncResult
*result
,
12057 ImageLoadContext
*load_context
)
12059 GFileInfo
*file_info
;
12060 GError
*error
= NULL
;
12062 file_info
= g_file_query_info_finish (file
, result
, &error
);
12064 image_load_context_free (load_context
);
12068 load_context
->content_type
= g_file_info_get_content_type (file_info
);
12069 load_context
->total_num_bytes
= g_file_info_get_size (file_info
);
12070 load_context
->filename
= g_file_info_get_name (file_info
);
12072 g_file_read_async (
12073 file
, G_PRIORITY_DEFAULT
,
12074 NULL
, (GAsyncReadyCallback
)
12075 image_load_file_read_cb
, load_context
);
12079 image_load_and_insert_async (EEditorPage
*editor_page
,
12080 const gchar
*selector
,
12083 ImageLoadContext
*load_context
;
12086 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12087 g_return_if_fail (uri
&& *uri
);
12089 file
= g_file_new_for_uri (uri
);
12090 g_return_if_fail (file
!= NULL
);
12092 load_context
= image_load_context_new (editor_page
);
12093 load_context
->file
= file
;
12094 if (selector
&& *selector
)
12095 load_context
->selector
= g_strdup (selector
);
12097 g_file_query_info_async (
12098 file
, "standard::*",
12099 G_FILE_QUERY_INFO_NONE
,G_PRIORITY_DEFAULT
,
12100 NULL
, (GAsyncReadyCallback
)
12101 image_load_query_info_cb
, load_context
);
12105 e_editor_dom_insert_image (EEditorPage
*editor_page
,
12108 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12110 if (!e_editor_page_get_html_mode (editor_page
))
12113 if (strstr (uri
, ";base64,")) {
12114 if (g_str_has_prefix (uri
, "data:"))
12115 e_editor_dom_insert_base64_image (editor_page
, uri
, "", "");
12116 if (strstr (uri
, ";data")) {
12117 const gchar
*base64_data
= strstr (uri
, ";") + 1;
12119 glong filename_length
;
12122 g_utf8_strlen (uri
, -1) -
12123 g_utf8_strlen (base64_data
, -1) - 1;
12124 filename
= g_strndup (uri
, filename_length
);
12126 e_editor_dom_insert_base64_image (editor_page
, base64_data
, filename
, "");
12130 image_load_and_insert_async (editor_page
, NULL
, uri
);
12134 e_editor_dom_replace_image_src (EEditorPage
*editor_page
,
12135 const gchar
*selector
,
12138 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12140 if (strstr (uri
, ";base64,")) {
12141 if (g_str_has_prefix (uri
, "data:"))
12142 e_editor_dom_replace_base64_image_src (
12143 editor_page
, selector
, uri
, "", "");
12144 if (strstr (uri
, ";data")) {
12145 const gchar
*base64_data
= strstr (uri
, ";") + 1;
12147 glong filename_length
;
12150 g_utf8_strlen (uri
, -1) -
12151 g_utf8_strlen (base64_data
, -1) - 1;
12152 filename
= g_strndup (uri
, filename_length
);
12154 e_editor_dom_replace_base64_image_src (
12155 editor_page
, selector
, base64_data
, filename
, "");
12159 image_load_and_insert_async (editor_page
, selector
, uri
);
12163 * e_html_editor_selection_unlink:
12164 * @selection: an #EEditorSelection
12166 * Removes any links (<A> elements) from current selection or at current
12170 e_editor_dom_selection_unlink (EEditorPage
*editor_page
)
12172 WebKitDOMDocument
*document
;
12173 WebKitDOMDOMWindow
*dom_window
= NULL
;
12174 WebKitDOMDOMSelection
*dom_selection
= NULL
;
12175 WebKitDOMRange
*range
= NULL
;
12176 WebKitDOMElement
*link
;
12177 EEditorUndoRedoManager
*manager
;
12180 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12182 document
= e_editor_page_get_document (editor_page
);
12183 dom_window
= webkit_dom_document_get_default_view (document
);
12184 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
12186 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
12187 link
= dom_node_find_parent_element (
12188 webkit_dom_range_get_start_container (range
, NULL
), "A");
12190 g_clear_object (&dom_selection
);
12191 g_clear_object (&dom_window
);
12194 WebKitDOMNode
*node
;
12196 /* get element that was clicked on */
12197 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
12198 if (node
&& !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
)) {
12199 link
= dom_node_find_parent_element (node
, "A");
12200 if (link
&& !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link
)) {
12201 g_clear_object (&range
);
12204 link
= WEBKIT_DOM_ELEMENT (node
);
12208 g_clear_object (&range
);
12213 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
12214 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
12215 EEditorHistoryEvent
*ev
;
12216 WebKitDOMDocumentFragment
*fragment
;
12218 ev
= g_new0 (EEditorHistoryEvent
, 1);
12219 ev
->type
= HISTORY_REMOVE_LINK
;
12221 e_editor_dom_selection_get_coordinates (editor_page
,
12222 &ev
->before
.start
.x
,
12223 &ev
->before
.start
.y
,
12225 &ev
->before
.end
.y
);
12227 fragment
= webkit_dom_document_create_document_fragment (document
);
12228 webkit_dom_node_append_child (
12229 WEBKIT_DOM_NODE (fragment
),
12230 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (link
), TRUE
, NULL
),
12232 ev
->data
.fragment
= g_object_ref (fragment
);
12234 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
12237 text
= webkit_dom_html_element_get_inner_text (
12238 WEBKIT_DOM_HTML_ELEMENT (link
));
12239 webkit_dom_element_set_outer_html (link
, text
, NULL
);
12244 * e_html_editor_selection_create_link:
12245 * @document: a @WebKitDOMDocument
12246 * @uri: destination of the new link
12248 * Converts current selection into a link pointing to @url.
12251 e_editor_dom_create_link (EEditorPage
*editor_page
,
12254 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12255 g_return_if_fail (uri
!= NULL
&& *uri
!= '\0');
12257 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_CREATE_LINK
, uri
);
12261 get_list_level (WebKitDOMNode
*node
)
12265 while (node
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
)) {
12266 if (node_is_list (node
))
12268 node
= webkit_dom_node_get_parent_node (node
);
12275 set_ordered_list_type_to_element (WebKitDOMElement
*list
,
12276 EContentEditorBlockFormat format
)
12278 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
)
12279 webkit_dom_element_remove_attribute (list
, "type");
12280 else if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
)
12281 webkit_dom_element_set_attribute (list
, "type", "A", NULL
);
12282 else if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN
)
12283 webkit_dom_element_set_attribute (list
, "type", "I", NULL
);
12286 static const gchar
*
12287 get_css_alignment_value_class (EContentEditorAlignment alignment
)
12289 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_LEFT
)
12290 return ""; /* Left is by default on ltr */
12292 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_CENTER
)
12293 return "-x-evo-align-center";
12295 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_RIGHT
)
12296 return "-x-evo-align-right";
12302 * e_html_editor_selection_get_alignment:
12303 * @selection: #an EEditorSelection
12305 * Returns alignment of current paragraph
12307 * Returns: #EContentEditorAlignment
12309 static EContentEditorAlignment
12310 dom_get_alignment (EEditorPage
*editor_page
)
12312 WebKitDOMDocument
*document
;
12313 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
12314 WebKitDOMDOMWindow
*dom_window
= NULL
;
12315 WebKitDOMElement
*element
;
12316 WebKitDOMNode
*node
;
12317 WebKitDOMRange
*range
= NULL
;
12318 EContentEditorAlignment alignment
;
12321 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_ALIGNMENT_LEFT
);
12323 document
= e_editor_page_get_document (editor_page
);
12324 range
= e_editor_dom_get_current_range (editor_page
);
12326 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12328 node
= webkit_dom_range_get_start_container (range
, NULL
);
12329 g_clear_object (&range
);
12331 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12333 if (WEBKIT_DOM_IS_ELEMENT (node
))
12334 element
= WEBKIT_DOM_ELEMENT (node
);
12336 element
= WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (node
));
12338 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (element
)) {
12339 if (element_has_class (element
, "-x-evo-align-right"))
12340 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12341 else if (element_has_class (element
, "-x-evo-align-center"))
12342 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12344 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12349 dom_window
= webkit_dom_document_get_default_view (document
);
12350 style
= webkit_dom_dom_window_get_computed_style (dom_window
, element
, NULL
);
12351 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
12353 if (!value
|| !*value
||
12354 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
12355 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12356 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
12357 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12358 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
12359 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12361 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12364 g_clear_object (&dom_window
);
12365 g_clear_object (&style
);
12372 set_word_wrap_length (EEditorPage
*editor_page
,
12373 gint user_word_wrap_length
)
12375 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
12377 /* user_word_wrap_length < 0, set block width to word_wrap_length
12378 * user_word_wrap_length == 0, no width limit set,
12379 * user_word_wrap_length > 0, set width limit to given value */
12380 return (user_word_wrap_length
< 0) ?
12381 e_editor_page_get_word_wrap_length (editor_page
) : user_word_wrap_length
;
12385 e_editor_dom_set_paragraph_style (EEditorPage
*editor_page
,
12386 WebKitDOMElement
*element
,
12389 const gchar
*style_to_add
)
12391 WebKitDOMNode
*parent
;
12392 gchar
*style
= NULL
;
12393 gint word_wrap_length
;
12395 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12397 word_wrap_length
= set_word_wrap_length (editor_page
, width
);
12398 webkit_dom_element_set_attribute (element
, "data-evo-paragraph", "", NULL
);
12400 /* Don't set the alignment for nodes as they are handled separately. */
12401 if (!node_is_list (WEBKIT_DOM_NODE (element
))) {
12402 EContentEditorAlignment alignment
;
12404 alignment
= dom_get_alignment (editor_page
);
12405 element_add_class (element
, get_css_alignment_value_class (alignment
));
12408 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
12409 /* Don't set the width limit to sub-blocks as the width limit is inhered
12410 * from its parents. */
12411 if (!e_editor_page_get_html_mode (editor_page
) &&
12412 (!parent
|| WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
))) {
12413 style
= g_strdup_printf (
12414 "width: %dch;%s%s",
12415 (word_wrap_length
+ offset
),
12416 style_to_add
&& *style_to_add
? " " : "",
12417 style_to_add
&& *style_to_add
? style_to_add
: "");
12419 if (style_to_add
&& *style_to_add
)
12420 style
= g_strdup_printf ("%s", style_to_add
);
12423 webkit_dom_element_set_attribute (element
, "style", style
, NULL
);
12428 static WebKitDOMElement
*
12429 create_list_element (EEditorPage
*editor_page
,
12430 EContentEditorBlockFormat format
,
12432 gboolean html_mode
)
12434 WebKitDOMDocument
*document
;
12435 WebKitDOMElement
*list
;
12436 gboolean inserting_unordered_list
;
12438 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12440 document
= e_editor_page_get_document (editor_page
);
12441 inserting_unordered_list
= format
== E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
12443 list
= webkit_dom_document_create_element (
12444 document
, inserting_unordered_list
? "UL" : "OL", NULL
);
12446 if (!inserting_unordered_list
)
12447 set_ordered_list_type_to_element (list
, format
);
12449 if (level
>= 0 && !html_mode
) {
12452 offset
= (level
+ 1) * SPACES_PER_LIST_LEVEL
;
12454 offset
+= !inserting_unordered_list
?
12455 SPACES_ORDERED_LIST_FIRST_LEVEL
- SPACES_PER_LIST_LEVEL
: 0;
12457 e_editor_dom_set_paragraph_style (editor_page
, list
, -1, -offset
, NULL
);
12464 indent_list (EEditorPage
*editor_page
)
12466 WebKitDOMDocument
*document
;
12467 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
12468 WebKitDOMNode
*item
, *next_item
;
12469 gboolean after_selection_end
= FALSE
;
12471 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
12473 document
= e_editor_page_get_document (editor_page
);
12474 selection_start_marker
= webkit_dom_document_get_element_by_id (
12475 document
, "-x-evo-selection-start-marker");
12476 selection_end_marker
= webkit_dom_document_get_element_by_id (
12477 document
, "-x-evo-selection-end-marker");
12479 item
= e_editor_dom_get_parent_block_node_from_child (
12480 WEBKIT_DOM_NODE (selection_start_marker
));
12482 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
12483 gboolean html_mode
= e_editor_page_get_html_mode (editor_page
);
12484 WebKitDOMElement
*list
;
12485 WebKitDOMNode
*source_list
= webkit_dom_node_get_parent_node (item
);
12486 EContentEditorBlockFormat format
;
12488 format
= dom_get_list_format_from_node (source_list
);
12490 list
= create_list_element (
12491 editor_page
, format
, get_list_level (item
), html_mode
);
12493 element_add_class (list
, "-x-evo-indented");
12495 webkit_dom_node_insert_before (
12496 source_list
, WEBKIT_DOM_NODE (list
), item
, NULL
);
12498 while (item
&& !after_selection_end
) {
12499 after_selection_end
= webkit_dom_node_contains (
12500 item
, WEBKIT_DOM_NODE (selection_end_marker
));
12502 next_item
= webkit_dom_node_get_next_sibling (item
);
12504 webkit_dom_node_append_child (
12505 WEBKIT_DOM_NODE (list
), item
, NULL
);
12510 merge_lists_if_possible (WEBKIT_DOM_NODE (list
));
12513 return after_selection_end
;
12517 dom_set_indented_style (EEditorPage
*editor_page
,
12518 WebKitDOMElement
*element
,
12522 gint word_wrap_length
;
12524 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12526 word_wrap_length
= set_word_wrap_length (editor_page
, width
);
12527 webkit_dom_element_set_class_name (element
, "-x-evo-indented");
12529 if (e_editor_page_get_html_mode (editor_page
) || word_wrap_length
== 0) {
12530 style
= g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION
);
12532 if (word_wrap_length
!= 0) {
12533 gchar
*plain_text_style
;
12535 plain_text_style
= g_strdup_printf (
12536 "margin-left: %dch; word-wrap: normal; width: %dch;",
12537 SPACES_PER_INDENTATION
, word_wrap_length
);
12539 webkit_dom_element_set_attribute (
12540 element
, "data-plain-text-style", plain_text_style
, NULL
);
12541 g_free (plain_text_style
);
12544 style
= g_strdup_printf (
12545 "margin-left: %dch; word-wrap: normal; width: %dch;",
12546 SPACES_PER_INDENTATION
, word_wrap_length
);
12549 webkit_dom_element_set_attribute (element
, "style", style
, NULL
);
12553 static WebKitDOMElement
*
12554 dom_get_indented_element (EEditorPage
*editor_page
,
12557 WebKitDOMDocument
*document
;
12558 WebKitDOMElement
*element
;
12560 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12562 document
= e_editor_page_get_document (editor_page
);
12563 element
= webkit_dom_document_create_element (document
, "DIV", NULL
);
12564 dom_set_indented_style (editor_page
, element
, width
);
12569 static WebKitDOMNode
*
12570 indent_block (EEditorPage
*editor_page
,
12571 WebKitDOMNode
*block
,
12574 WebKitDOMElement
*element
;
12575 WebKitDOMNode
*sibling
, *tmp
;
12577 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12579 sibling
= webkit_dom_node_get_previous_sibling (block
);
12580 if (WEBKIT_DOM_IS_ELEMENT (sibling
) &&
12581 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-indented")) {
12582 element
= WEBKIT_DOM_ELEMENT (sibling
);
12584 element
= dom_get_indented_element (editor_page
, width
);
12586 webkit_dom_node_insert_before (
12587 webkit_dom_node_get_parent_node (block
),
12588 WEBKIT_DOM_NODE (element
),
12593 /* Remove style and let the paragraph inherit it from parent */
12594 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph"))
12595 webkit_dom_element_remove_attribute (
12596 WEBKIT_DOM_ELEMENT (block
), "style");
12598 tmp
= webkit_dom_node_append_child (
12599 WEBKIT_DOM_NODE (element
),
12603 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
12605 while (WEBKIT_DOM_IS_ELEMENT (sibling
) &&
12606 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-indented")) {
12607 WebKitDOMNode
*next_sibling
;
12608 WebKitDOMNode
*child
;
12610 next_sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (sibling
));
12612 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (sibling
)))) {
12613 webkit_dom_node_append_child (
12614 WEBKIT_DOM_NODE (element
),
12618 remove_node (sibling
);
12619 sibling
= next_sibling
;
12625 static WebKitDOMNode
*
12626 get_list_item_node_from_child (WebKitDOMNode
*child
)
12628 WebKitDOMNode
*parent
= webkit_dom_node_get_parent_node (child
);
12630 while (parent
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent
))
12631 parent
= webkit_dom_node_get_parent_node (parent
);
12636 static WebKitDOMNode
*
12637 get_list_node_from_child (WebKitDOMNode
*child
)
12639 WebKitDOMNode
*parent
= get_list_item_node_from_child (child
);
12641 return webkit_dom_node_get_parent_node (parent
);
12645 do_format_change_list_to_block (EEditorPage
*editor_page
,
12646 EContentEditorBlockFormat format
,
12647 WebKitDOMNode
*item
,
12648 const gchar
*value
)
12650 WebKitDOMDocument
*document
;
12651 WebKitDOMElement
*element
, *selection_end
;
12652 WebKitDOMNode
*node
, *source_list
;
12653 gboolean after_end
= FALSE
;
12656 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
12658 document
= e_editor_page_get_document (editor_page
);
12659 selection_end
= webkit_dom_document_get_element_by_id (
12660 document
, "-x-evo-selection-end-marker");
12662 source_list
= webkit_dom_node_get_parent_node (item
);
12663 while (source_list
) {
12664 WebKitDOMNode
*parent
;
12666 parent
= webkit_dom_node_get_parent_node (source_list
);
12667 if (node_is_list (parent
))
12668 source_list
= parent
;
12673 if (webkit_dom_node_contains (source_list
, WEBKIT_DOM_NODE (selection_end
)))
12674 source_list
= split_list_into_two (item
, -1);
12676 source_list
= webkit_dom_node_get_next_sibling (source_list
);
12679 /* Process all nodes that are in selection one by one */
12680 while (item
&& WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
12681 WebKitDOMNode
*next_item
;
12683 next_item
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (item
));
12685 WebKitDOMNode
*parent
;
12686 WebKitDOMNode
*tmp
= item
;
12689 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp
));
12690 if (!node_is_list (parent
))
12693 next_item
= webkit_dom_node_get_next_sibling (parent
);
12694 if (node_is_list (next_item
)) {
12695 next_item
= webkit_dom_node_get_first_child (next_item
);
12697 } else if (next_item
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12698 next_item
= webkit_dom_node_get_next_sibling (next_item
);
12700 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12705 } else if (node_is_list (next_item
)) {
12706 next_item
= webkit_dom_node_get_first_child (next_item
);
12707 } else if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12708 next_item
= webkit_dom_node_get_next_sibling (item
);
12713 after_end
= webkit_dom_node_contains (item
, WEBKIT_DOM_NODE (selection_end
));
12715 level
= get_indentation_level (WEBKIT_DOM_ELEMENT (item
));
12717 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
) {
12718 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
12720 element
= webkit_dom_document_create_element (
12721 document
, value
, NULL
);
12723 while ((node
= webkit_dom_node_get_first_child (item
)))
12724 webkit_dom_node_append_child (
12725 WEBKIT_DOM_NODE (element
), node
, NULL
);
12727 webkit_dom_node_insert_before (
12728 webkit_dom_node_get_parent_node (source_list
),
12729 WEBKIT_DOM_NODE (element
),
12734 gint final_width
= 0;
12736 node
= WEBKIT_DOM_NODE (element
);
12738 if (webkit_dom_element_has_attribute (element
, "data-evo-paragraph"))
12739 final_width
= e_editor_page_get_word_wrap_length (editor_page
) -
12740 SPACES_PER_INDENTATION
* level
;
12743 node
= indent_block (editor_page
, node
, final_width
);
12746 e_editor_dom_remove_node_and_parents_if_empty (item
);
12753 remove_node_if_empty (source_list
);
12759 format_change_list_to_block (EEditorPage
*editor_page
,
12760 EContentEditorBlockFormat format
,
12761 const gchar
*value
)
12763 WebKitDOMDocument
*document
;
12764 WebKitDOMElement
*selection_start
;
12765 WebKitDOMNode
*item
;
12767 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12769 document
= e_editor_page_get_document (editor_page
);
12771 selection_start
= webkit_dom_document_get_element_by_id (
12772 document
, "-x-evo-selection-start-marker");
12774 item
= get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start
));
12776 do_format_change_list_to_block (editor_page
, format
, item
, value
);
12779 static WebKitDOMNode
*
12780 get_parent_indented_block (WebKitDOMNode
*node
)
12782 WebKitDOMNode
*parent
, *block
= NULL
;
12784 parent
= webkit_dom_node_get_parent_node (node
);
12785 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-indented"))
12788 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
12789 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-indented"))
12791 parent
= webkit_dom_node_get_parent_node (parent
);
12797 static WebKitDOMElement
*
12798 get_element_for_inspection (WebKitDOMRange
*range
)
12800 WebKitDOMNode
*node
;
12802 node
= webkit_dom_range_get_end_container (range
, NULL
);
12803 /* No selection or whole body selected */
12804 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
12807 return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
12810 static EContentEditorAlignment
12811 dom_get_alignment_from_node (WebKitDOMNode
*node
)
12813 EContentEditorAlignment alignment
;
12815 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
12817 style
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node
));
12818 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
12820 if (!value
|| !*value
||
12821 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
12822 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12823 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
12824 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12825 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
12826 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12828 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12831 g_clear_object (&style
);
12838 * e_html_editor_selection_indent:
12839 * @selection: an #EEditorSelection
12841 * Indents current paragraph by one level.
12844 e_editor_dom_selection_indent (EEditorPage
*editor_page
)
12846 WebKitDOMDocument
*document
;
12847 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
12848 WebKitDOMNode
*block
;
12849 EEditorHistoryEvent
*ev
= NULL
;
12850 EEditorUndoRedoManager
*manager
;
12851 gboolean after_selection_start
= FALSE
, after_selection_end
= FALSE
;
12853 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12855 document
= e_editor_page_get_document (editor_page
);
12856 e_editor_dom_selection_save (editor_page
);
12858 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
12860 selection_start_marker
= webkit_dom_document_get_element_by_id (
12861 document
, "-x-evo-selection-start-marker");
12862 selection_end_marker
= webkit_dom_document_get_element_by_id (
12863 document
, "-x-evo-selection-end-marker");
12865 /* If the selection was not saved, move it into the first child of body */
12866 if (!selection_start_marker
|| !selection_end_marker
) {
12867 WebKitDOMHTMLElement
*body
;
12868 WebKitDOMNode
*child
;
12870 body
= webkit_dom_document_get_body (document
);
12871 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
12873 dom_add_selection_markers_into_element_start (
12875 WEBKIT_DOM_ELEMENT (child
),
12876 &selection_start_marker
,
12877 &selection_end_marker
);
12880 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
12881 ev
= g_new0 (EEditorHistoryEvent
, 1);
12882 ev
->type
= HISTORY_INDENT
;
12884 e_editor_dom_selection_get_coordinates (editor_page
,
12885 &ev
->before
.start
.x
,
12886 &ev
->before
.start
.y
,
12888 &ev
->before
.end
.y
);
12890 ev
->data
.style
.from
= 1;
12891 ev
->data
.style
.to
= 1;
12894 block
= get_parent_indented_block (
12895 WEBKIT_DOM_NODE (selection_start_marker
));
12897 block
= e_editor_dom_get_parent_block_node_from_child (
12898 WEBKIT_DOM_NODE (selection_start_marker
));
12900 while (block
&& !after_selection_end
) {
12901 gint ii
, length
, level
, word_wrap_length
, final_width
= 0;
12902 WebKitDOMNode
*next_block
;
12903 WebKitDOMNodeList
*list
= NULL
;
12905 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
12907 next_block
= webkit_dom_node_get_next_sibling (block
);
12909 list
= webkit_dom_element_query_selector_all (
12910 WEBKIT_DOM_ELEMENT (block
),
12911 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
12914 after_selection_end
= webkit_dom_node_contains (
12915 block
, WEBKIT_DOM_NODE (selection_end_marker
));
12917 length
= webkit_dom_node_list_get_length (list
);
12918 if (length
== 0 && node_is_list_or_item (block
)) {
12919 after_selection_end
= indent_list (editor_page
);
12924 if (!after_selection_start
) {
12925 after_selection_start
= webkit_dom_node_contains (
12926 block
, WEBKIT_DOM_NODE (selection_start_marker
));
12927 if (!after_selection_start
)
12931 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph")) {
12932 level
= get_indentation_level (WEBKIT_DOM_ELEMENT (block
));
12934 final_width
= word_wrap_length
- SPACES_PER_INDENTATION
* (level
+ 1);
12935 if (final_width
< MINIMAL_PARAGRAPH_WIDTH
&&
12936 !e_editor_page_get_html_mode (editor_page
))
12940 indent_block (editor_page
, block
, final_width
);
12942 if (after_selection_end
)
12946 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
12947 WebKitDOMNode
*block_to_process
;
12949 block_to_process
= webkit_dom_node_list_item (list
, ii
);
12951 after_selection_end
= webkit_dom_node_contains (
12952 block_to_process
, WEBKIT_DOM_NODE (selection_end_marker
));
12954 if (!after_selection_start
) {
12955 after_selection_start
= webkit_dom_node_contains (
12957 WEBKIT_DOM_NODE (selection_start_marker
));
12958 if (!after_selection_start
)
12962 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block_to_process
), "data-evo-paragraph")) {
12963 level
= get_indentation_level (
12964 WEBKIT_DOM_ELEMENT (block_to_process
));
12966 final_width
= word_wrap_length
- SPACES_PER_INDENTATION
* (level
+ 1);
12967 if (final_width
< MINIMAL_PARAGRAPH_WIDTH
&&
12968 !e_editor_page_get_html_mode (editor_page
))
12972 indent_block (editor_page
, block_to_process
, final_width
);
12974 if (after_selection_end
)
12979 g_clear_object (&list
);
12981 if (!after_selection_end
)
12982 block
= next_block
;
12986 e_editor_dom_selection_get_coordinates (editor_page
,
12987 &ev
->after
.start
.x
,
12988 &ev
->after
.start
.y
,
12991 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
12994 e_editor_dom_selection_restore (editor_page
);
12995 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
12996 e_editor_page_emit_content_changed (editor_page
);
13000 unindent_list (WebKitDOMDocument
*document
)
13002 gboolean after
= FALSE
;
13003 WebKitDOMElement
*new_list
;
13004 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
13005 WebKitDOMNode
*source_list
, *source_list_clone
, *current_list
, *item
;
13006 WebKitDOMNode
*prev_item
;
13008 selection_start_marker
= webkit_dom_document_get_element_by_id (
13009 document
, "-x-evo-selection-start-marker");
13010 selection_end_marker
= webkit_dom_document_get_element_by_id (
13011 document
, "-x-evo-selection-end-marker");
13013 if (!selection_start_marker
|| !selection_end_marker
)
13016 /* Copy elements from previous block to list */
13017 item
= e_editor_dom_get_parent_block_node_from_child (
13018 WEBKIT_DOM_NODE (selection_start_marker
));
13019 source_list
= webkit_dom_node_get_parent_node (item
);
13020 new_list
= WEBKIT_DOM_ELEMENT (
13021 webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
));
13022 current_list
= source_list
;
13023 source_list_clone
= webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
);
13025 webkit_dom_node_insert_before (
13026 webkit_dom_node_get_parent_node (source_list
),
13027 WEBKIT_DOM_NODE (source_list_clone
),
13028 webkit_dom_node_get_next_sibling (source_list
),
13031 if (element_has_class (WEBKIT_DOM_ELEMENT (source_list
), "-x-evo-indented"))
13032 element_add_class (WEBKIT_DOM_ELEMENT (new_list
), "-x-evo-indented");
13034 prev_item
= source_list
;
13037 WebKitDOMNode
*next_item
= webkit_dom_node_get_next_sibling (item
);
13039 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
13041 prev_item
= webkit_dom_node_append_child (
13042 source_list_clone
, WEBKIT_DOM_NODE (item
), NULL
);
13044 prev_item
= webkit_dom_node_insert_before (
13045 webkit_dom_node_get_parent_node (prev_item
),
13047 webkit_dom_node_get_next_sibling (prev_item
),
13051 if (webkit_dom_node_contains (item
, WEBKIT_DOM_NODE (selection_end_marker
)))
13058 current_list
= webkit_dom_node_get_next_sibling (current_list
);
13059 next_item
= webkit_dom_node_get_first_child (current_list
);
13064 remove_node_if_empty (source_list_clone
);
13065 remove_node_if_empty (source_list
);
13069 unindent_block (EEditorPage
*editor_page
,
13070 WebKitDOMNode
*block
)
13072 WebKitDOMElement
*element
;
13073 WebKitDOMElement
*prev_blockquote
= NULL
, *next_blockquote
= NULL
;
13074 WebKitDOMNode
*block_to_process
, *node_clone
= NULL
, *child
;
13075 EContentEditorAlignment alignment
;
13076 gboolean before_node
= TRUE
;
13077 gint word_wrap_length
, level
, width
;
13079 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13081 block_to_process
= block
;
13083 alignment
= dom_get_alignment_from_node (block_to_process
);
13084 element
= webkit_dom_node_get_parent_element (block_to_process
);
13086 if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element
) &&
13087 !element_has_class (element
, "-x-evo-indented"))
13090 element_add_class (WEBKIT_DOM_ELEMENT (block_to_process
), "-x-evo-to-unindent");
13092 level
= get_indentation_level (element
);
13093 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
13094 width
= word_wrap_length
- SPACES_PER_INDENTATION
* level
;
13096 /* Look if we have previous siblings, if so, we have to
13097 * create new blockquote that will include them */
13098 if (webkit_dom_node_get_previous_sibling (block_to_process
))
13099 prev_blockquote
= dom_get_indented_element (editor_page
, width
);
13101 /* Look if we have next siblings, if so, we have to
13102 * create new blockquote that will include them */
13103 if (webkit_dom_node_get_next_sibling (block_to_process
))
13104 next_blockquote
= dom_get_indented_element (editor_page
, width
);
13106 /* Copy nodes that are before / after the element that we want to unindent */
13107 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
)))) {
13108 if (webkit_dom_node_is_equal_node (child
, block_to_process
)) {
13109 before_node
= FALSE
;
13110 node_clone
= webkit_dom_node_clone_node_with_error (child
, TRUE
, NULL
);
13111 remove_node (child
);
13115 webkit_dom_node_append_child (
13117 WEBKIT_DOM_NODE (prev_blockquote
) :
13118 WEBKIT_DOM_NODE (next_blockquote
),
13124 element_remove_class (WEBKIT_DOM_ELEMENT (node_clone
), "-x-evo-to-unindent");
13126 /* Insert blockqoute with nodes that were before the element that we want to unindent */
13127 if (prev_blockquote
) {
13128 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote
))) {
13129 webkit_dom_node_insert_before (
13130 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13131 WEBKIT_DOM_NODE (prev_blockquote
),
13132 WEBKIT_DOM_NODE (element
),
13137 if (level
== 1 && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node_clone
), "data-evo-paragraph")) {
13138 e_editor_dom_set_paragraph_style (
13139 editor_page
, WEBKIT_DOM_ELEMENT (node_clone
), word_wrap_length
, 0, NULL
);
13140 element_add_class (
13141 WEBKIT_DOM_ELEMENT (node_clone
),
13142 get_css_alignment_value_class (alignment
));
13145 /* Insert the unindented element */
13146 webkit_dom_node_insert_before (
13147 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13149 WEBKIT_DOM_NODE (element
),
13152 g_warn_if_reached ();
13155 /* Insert blockqoute with nodes that were after the element that we want to unindent */
13156 if (next_blockquote
) {
13157 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote
))) {
13158 webkit_dom_node_insert_before (
13159 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13160 WEBKIT_DOM_NODE (next_blockquote
),
13161 WEBKIT_DOM_NODE (element
),
13166 /* Remove old blockquote */
13167 remove_node (WEBKIT_DOM_NODE (element
));
13172 * @selection: an #EEditorSelection
13174 * Unindents current paragraph by one level.
13177 e_editor_dom_selection_unindent (EEditorPage
*editor_page
)
13179 WebKitDOMDocument
*document
;
13180 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
13181 WebKitDOMNode
*block
;
13182 EEditorHistoryEvent
*ev
= NULL
;
13183 EEditorUndoRedoManager
*manager
;
13184 gboolean after_selection_start
= FALSE
, after_selection_end
= FALSE
;
13186 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13188 document
= e_editor_page_get_document (editor_page
);
13189 e_editor_dom_selection_save (editor_page
);
13191 selection_start_marker
= webkit_dom_document_get_element_by_id (
13192 document
, "-x-evo-selection-start-marker");
13193 selection_end_marker
= webkit_dom_document_get_element_by_id (
13194 document
, "-x-evo-selection-end-marker");
13196 /* If the selection was not saved, move it into the first child of body */
13197 if (!selection_start_marker
|| !selection_end_marker
) {
13198 WebKitDOMHTMLElement
*body
;
13199 WebKitDOMNode
*child
;
13201 body
= webkit_dom_document_get_body (document
);
13202 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
13204 dom_add_selection_markers_into_element_start (
13206 WEBKIT_DOM_ELEMENT (child
),
13207 &selection_start_marker
,
13208 &selection_end_marker
);
13211 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
13212 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
13213 ev
= g_new0 (EEditorHistoryEvent
, 1);
13214 ev
->type
= HISTORY_INDENT
;
13216 e_editor_dom_selection_get_coordinates (editor_page
,
13217 &ev
->before
.start
.x
,
13218 &ev
->before
.start
.y
,
13220 &ev
->before
.end
.y
);
13223 block
= get_parent_indented_block (
13224 WEBKIT_DOM_NODE (selection_start_marker
));
13226 block
= e_editor_dom_get_parent_block_node_from_child (
13227 WEBKIT_DOM_NODE (selection_start_marker
));
13229 while (block
&& !after_selection_end
) {
13231 WebKitDOMNode
*next_block
;
13232 WebKitDOMNodeList
*list
= NULL
;
13234 next_block
= webkit_dom_node_get_next_sibling (block
);
13236 list
= webkit_dom_element_query_selector_all (
13237 WEBKIT_DOM_ELEMENT (block
),
13238 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
13241 after_selection_end
= webkit_dom_node_contains (
13242 block
, WEBKIT_DOM_NODE (selection_end_marker
));
13244 length
= webkit_dom_node_list_get_length (list
);
13245 if (length
== 0 && node_is_list_or_item (block
)) {
13246 unindent_list (document
);
13251 if (!after_selection_start
) {
13252 after_selection_start
= webkit_dom_node_contains (
13253 block
, WEBKIT_DOM_NODE (selection_start_marker
));
13254 if (!after_selection_start
)
13258 unindent_block (editor_page
, block
);
13260 if (after_selection_end
)
13264 for (ii
= 0; ii
< length
; ii
++) {
13265 WebKitDOMNode
*block_to_process
;
13267 block_to_process
= webkit_dom_node_list_item (list
, ii
);
13269 after_selection_end
= webkit_dom_node_contains (
13271 WEBKIT_DOM_NODE (selection_end_marker
));
13273 if (!after_selection_start
) {
13274 after_selection_start
= webkit_dom_node_contains (
13276 WEBKIT_DOM_NODE (selection_start_marker
));
13277 if (!after_selection_start
)
13281 unindent_block (editor_page
, block_to_process
);
13283 if (after_selection_end
)
13287 g_clear_object (&list
);
13288 block
= next_block
;
13292 e_editor_dom_selection_get_coordinates (editor_page
,
13293 &ev
->after
.start
.x
,
13294 &ev
->after
.start
.y
,
13297 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
13300 e_editor_dom_selection_restore (editor_page
);
13302 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
13303 e_editor_page_emit_content_changed (editor_page
);
13307 dom_insert_selection_point (WebKitDOMNode
*container
,
13309 WebKitDOMElement
*selection_point
)
13311 WebKitDOMNode
*parent
;
13313 parent
= webkit_dom_node_get_parent_node (container
);
13315 if (WEBKIT_DOM_IS_TEXT (container
) ||
13316 WEBKIT_DOM_IS_COMMENT (container
) ||
13317 WEBKIT_DOM_IS_CHARACTER_DATA (container
)) {
13319 WebKitDOMText
*split_text
;
13321 split_text
= webkit_dom_text_split_text (
13322 WEBKIT_DOM_TEXT (container
), offset
, NULL
);
13323 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (split_text
));
13325 webkit_dom_node_insert_before (
13327 WEBKIT_DOM_NODE (selection_point
),
13328 WEBKIT_DOM_NODE (split_text
),
13331 webkit_dom_node_insert_before (
13333 WEBKIT_DOM_NODE (selection_point
),
13338 gulong child_element_count
= 0;
13340 child_element_count
=
13341 webkit_dom_element_get_child_element_count (
13342 WEBKIT_DOM_ELEMENT (container
));
13345 /* Selection point is on the beginning of container */
13346 webkit_dom_node_insert_before (
13348 WEBKIT_DOM_NODE (selection_point
),
13349 webkit_dom_node_get_first_child (container
),
13351 } else if (offset
!= 0 && (offset
== child_element_count
)) {
13352 /* Selection point is on the end of container */
13353 webkit_dom_node_append_child (
13354 container
, WEBKIT_DOM_NODE (selection_point
), NULL
);
13356 WebKitDOMElement
*child
;
13359 child
= webkit_dom_element_get_first_element_child (WEBKIT_DOM_ELEMENT (container
));
13360 for (ii
= 1; ii
< child_element_count
; ii
++)
13361 child
= webkit_dom_element_get_next_element_sibling (child
);
13363 webkit_dom_node_insert_before (
13365 WEBKIT_DOM_NODE (selection_point
),
13366 WEBKIT_DOM_NODE (child
),
13371 webkit_dom_node_normalize (parent
);
13375 * e_html_editor_selection_save:
13376 * @selection: an #EEditorSelection
13378 * Saves current cursor position or current selection range. The selection can
13379 * be later restored by calling e_html_editor_selection_restore().
13381 * Note that calling e_html_editor_selection_save() overwrites previously saved
13384 * Note that this method inserts special markings into the HTML code that are
13385 * used to later restore the selection. It can happen that by deleting some
13386 * segments of the document some of the markings are deleted too. In that case
13387 * restoring the selection by e_html_editor_selection_restore() can fail. Also by
13388 * moving text segments (Cut & Paste) can result in moving the markings
13389 * elsewhere, thus e_html_editor_selection_restore() will restore the selection
13392 * It is recommended to use this method only when you are not planning to make
13393 * bigger changes to content or structure of the document (formatting changes
13397 e_editor_dom_selection_save (EEditorPage
*editor_page
)
13399 WebKitDOMDocument
*document
;
13400 WebKitDOMDOMWindow
*dom_window
= NULL
;
13401 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13402 WebKitDOMRange
*range
= NULL
;
13403 WebKitDOMNode
*container
;
13404 WebKitDOMNode
*anchor
;
13405 WebKitDOMElement
*start_marker
= NULL
, *end_marker
= NULL
;
13406 gboolean collapsed
= FALSE
;
13407 glong offset
, anchor_offset
;
13409 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13411 document
= e_editor_page_get_document (editor_page
);
13413 /* First remove all markers (if present) */
13414 dom_remove_selection_markers (document
);
13416 dom_window
= webkit_dom_document_get_default_view (document
);
13417 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
13418 g_clear_object (&dom_window
);
13420 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
13421 g_clear_object (&dom_selection
);
13425 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
13427 g_clear_object (&dom_selection
);
13431 anchor
= webkit_dom_dom_selection_get_anchor_node (dom_selection
);
13432 anchor_offset
= webkit_dom_dom_selection_get_anchor_offset (dom_selection
);
13434 collapsed
= webkit_dom_range_get_collapsed (range
, NULL
);
13435 start_marker
= dom_create_selection_marker (document
, TRUE
);
13437 container
= webkit_dom_range_get_start_container (range
, NULL
);
13438 offset
= webkit_dom_range_get_start_offset (range
, NULL
);
13440 if (webkit_dom_node_is_same_node (anchor
, container
) && offset
== anchor_offset
)
13441 webkit_dom_element_set_attribute (start_marker
, "data-anchor", "", NULL
);
13443 dom_insert_selection_point (container
, offset
, start_marker
);
13445 end_marker
= dom_create_selection_marker (document
, FALSE
);
13448 webkit_dom_node_insert_before (
13449 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker
)),
13450 WEBKIT_DOM_NODE (end_marker
),
13451 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker
)),
13456 container
= webkit_dom_range_get_end_container (range
, NULL
);
13457 offset
= webkit_dom_range_get_end_offset (range
, NULL
);
13459 if (webkit_dom_node_is_same_node (anchor
, container
) && offset
== anchor_offset
)
13460 webkit_dom_element_set_attribute (end_marker
, "data-anchor", "", NULL
);
13462 dom_insert_selection_point (container
, offset
, end_marker
);
13465 if (start_marker
&& end_marker
) {
13466 webkit_dom_range_set_start_after (range
, WEBKIT_DOM_NODE (start_marker
), NULL
);
13467 webkit_dom_range_set_end_before (range
, WEBKIT_DOM_NODE (end_marker
), NULL
);
13469 g_warn_if_reached ();
13472 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
13473 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13476 g_clear_object (&range
);
13477 g_clear_object (&dom_selection
);
13481 e_editor_dom_is_selection_position_node (WebKitDOMNode
*node
)
13483 WebKitDOMElement
*element
;
13485 if (!node
|| !WEBKIT_DOM_IS_ELEMENT (node
))
13488 element
= WEBKIT_DOM_ELEMENT (node
);
13490 return element_has_id (element
, "-x-evo-selection-start-marker") ||
13491 element_has_id (element
, "-x-evo-selection-end-marker");
13495 * e_html_editor_selection_restore:
13496 * @selection: an #EEditorSelection
13498 * Restores cursor position or selection range that was saved by
13499 * e_html_editor_selection_save().
13501 * Note that calling this function without calling e_html_editor_selection_save()
13502 * before is a programming error and the behavior is undefined.
13505 e_editor_dom_selection_restore (EEditorPage
*editor_page
)
13507 WebKitDOMDocument
*document
;
13508 WebKitDOMElement
*marker
;
13509 WebKitDOMNode
*selection_start_marker
, *selection_end_marker
;
13510 WebKitDOMNode
*parent_start
, *parent_end
, *anchor
;
13511 WebKitDOMRange
*range
= NULL
;
13512 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13513 WebKitDOMDOMWindow
*dom_window
= NULL
;
13514 gboolean start_is_anchor
= FALSE
;
13517 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13519 document
= e_editor_page_get_document (editor_page
);
13520 dom_window
= webkit_dom_document_get_default_view (document
);
13521 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
13522 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
13523 g_clear_object (&dom_window
);
13525 WebKitDOMHTMLElement
*body
;
13527 range
= webkit_dom_document_create_range (document
);
13528 body
= webkit_dom_document_get_body (document
);
13530 webkit_dom_range_select_node_contents (range
, WEBKIT_DOM_NODE (body
), NULL
);
13531 webkit_dom_range_collapse (range
, TRUE
, NULL
);
13532 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13535 selection_start_marker
= webkit_dom_range_get_start_container (range
, NULL
);
13536 if (selection_start_marker
) {
13537 gboolean ok
= FALSE
;
13538 selection_start_marker
=
13539 webkit_dom_node_get_next_sibling (selection_start_marker
);
13541 ok
= e_editor_dom_is_selection_position_node (selection_start_marker
);
13545 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
13546 selection_end_marker
= webkit_dom_node_get_next_sibling (
13547 selection_start_marker
);
13549 ok
= e_editor_dom_is_selection_position_node (selection_end_marker
);
13551 WebKitDOMNode
*next_sibling
;
13553 next_sibling
= webkit_dom_node_get_next_sibling (selection_end_marker
);
13555 if (next_sibling
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
)) {
13556 parent_start
= webkit_dom_node_get_parent_node (selection_end_marker
);
13558 remove_node (selection_start_marker
);
13559 remove_node (selection_end_marker
);
13561 webkit_dom_node_normalize (parent_start
);
13562 g_clear_object (&range
);
13563 g_clear_object (&dom_selection
);
13571 g_clear_object (&range
);
13572 range
= webkit_dom_document_create_range (document
);
13574 g_clear_object (&dom_selection
);
13578 marker
= webkit_dom_document_get_element_by_id (
13579 document
, "-x-evo-selection-start-marker");
13581 marker
= webkit_dom_document_get_element_by_id (
13582 document
, "-x-evo-selection-end-marker");
13584 remove_node (WEBKIT_DOM_NODE (marker
));
13585 g_clear_object (&dom_selection
);
13586 g_clear_object (&range
);
13590 start_is_anchor
= webkit_dom_element_has_attribute (marker
, "data-anchor");
13591 parent_start
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker
));
13593 webkit_dom_range_set_start_after (range
, WEBKIT_DOM_NODE (marker
), NULL
);
13594 remove_node (WEBKIT_DOM_NODE (marker
));
13596 marker
= webkit_dom_document_get_element_by_id (
13597 document
, "-x-evo-selection-end-marker");
13599 marker
= webkit_dom_document_get_element_by_id (
13600 document
, "-x-evo-selection-start-marker");
13602 remove_node (WEBKIT_DOM_NODE (marker
));
13603 g_clear_object (&dom_selection
);
13604 g_clear_object (&range
);
13608 parent_end
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker
));
13610 webkit_dom_range_set_end_before (range
, WEBKIT_DOM_NODE (marker
), NULL
);
13611 remove_node (WEBKIT_DOM_NODE (marker
));
13613 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
13614 if (webkit_dom_node_is_same_node (parent_start
, parent_end
))
13615 webkit_dom_node_normalize (parent_start
);
13617 webkit_dom_node_normalize (parent_start
);
13618 webkit_dom_node_normalize (parent_end
);
13621 if (start_is_anchor
) {
13622 anchor
= webkit_dom_range_get_end_container (range
, NULL
);
13623 offset
= webkit_dom_range_get_end_offset (range
, NULL
);
13625 webkit_dom_range_collapse (range
, TRUE
, NULL
);
13627 anchor
= webkit_dom_range_get_start_container (range
, NULL
);
13628 offset
= webkit_dom_range_get_start_offset (range
, NULL
);
13630 webkit_dom_range_collapse (range
, FALSE
, NULL
);
13632 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13633 webkit_dom_dom_selection_extend (dom_selection
, anchor
, offset
, NULL
);
13635 g_clear_object (&dom_selection
);
13636 g_clear_object (&range
);
13640 find_where_to_break_line (WebKitDOMCharacterData
*node
,
13643 gboolean last_break_position_is_dash
= FALSE
;
13644 gchar
*str
, *text_start
;
13646 gint pos
= 1, last_break_position
= 0, ret_val
= 0;
13648 text_start
= webkit_dom_character_data_get_data (node
);
13652 uc
= g_utf8_get_char (str
);
13654 ret_val
= pos
<= max_length
? pos
: last_break_position
> 0 ? last_break_position
- 1 : 0;
13658 if ((g_unichar_isspace (uc
) && !(g_unichar_break_type (uc
) == G_UNICODE_BREAK_NON_BREAKING_GLUE
)) ||
13660 if ((last_break_position_is_dash
= *str
== '-')) {
13661 /* There was no space before the dash */
13662 if (pos
- 1 != last_break_position
) {
13665 rest
= g_utf8_next_char (str
);
13666 if (rest
&& *rest
) {
13667 gunichar next_char
;
13669 /* There is no space after the dash */
13670 next_char
= g_utf8_get_char (rest
);
13671 if (g_unichar_isspace (next_char
))
13672 last_break_position_is_dash
= FALSE
;
13674 last_break_position
= pos
;
13676 last_break_position_is_dash
= FALSE
;
13678 last_break_position_is_dash
= FALSE
;
13680 last_break_position
= pos
;
13683 if ((pos
== max_length
)) {
13684 /* Look one character after the limit to check if there
13685 * is a space (skip dash) that we are allowed to break at, if so
13686 * break it there. */
13688 str
= g_utf8_next_char (str
);
13689 uc
= g_utf8_get_char (str
);
13691 if ((g_unichar_isspace (uc
) &&
13692 !(g_unichar_break_type (uc
) == G_UNICODE_BREAK_NON_BREAKING_GLUE
)))
13693 last_break_position
= ++pos
;
13699 str
= g_utf8_next_char (str
);
13702 if (last_break_position
!= 0)
13703 ret_val
= last_break_position
- 1;
13705 g_free (text_start
);
13707 /* Always break after the dash character. */
13708 if (last_break_position_is_dash
)
13711 /* No character to break at is found. We should split at max_length, but
13712 * we will leave the decision on caller as it depends on context. */
13713 if (ret_val
== 0 && last_break_position
== 0)
13720 * e_html_editor_selection_is_collapsed:
13721 * @selection: an #EEditorSelection
13723 * Returns if selection is collapsed.
13725 * Returns: Whether the selection is collapsed (just caret) or not (someting is selected).
13728 e_editor_dom_selection_is_collapsed (EEditorPage
*editor_page
)
13730 WebKitDOMDocument
*document
;
13731 WebKitDOMDOMWindow
*dom_window
= NULL
;
13732 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13733 gboolean collapsed
;
13735 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
13737 document
= e_editor_page_get_document (editor_page
);
13738 if (!(dom_window
= webkit_dom_document_get_default_view (document
)))
13741 if (!(dom_selection
= webkit_dom_dom_window_get_selection (dom_window
))) {
13742 g_clear_object (&dom_window
);
13746 collapsed
= webkit_dom_dom_selection_get_is_collapsed (dom_selection
);
13748 g_clear_object (&dom_selection
);
13754 e_editor_dom_scroll_to_caret (EEditorPage
*editor_page
)
13756 WebKitDOMDocument
*document
;
13757 WebKitDOMDOMWindow
*dom_window
= NULL
;
13758 WebKitDOMElement
*selection_start_marker
;
13759 glong element_top
, element_left
;
13760 glong window_top
, window_left
, window_right
, window_bottom
;
13762 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13764 document
= e_editor_page_get_document (editor_page
);
13765 e_editor_dom_selection_save (editor_page
);
13767 selection_start_marker
= webkit_dom_document_get_element_by_id (
13768 document
, "-x-evo-selection-start-marker");
13769 if (!selection_start_marker
)
13772 dom_window
= webkit_dom_document_get_default_view (document
);
13774 window_top
= webkit_dom_dom_window_get_scroll_y (dom_window
);
13775 window_left
= webkit_dom_dom_window_get_scroll_x (dom_window
);
13776 window_bottom
= window_top
+ webkit_dom_dom_window_get_inner_height (dom_window
);
13777 window_right
= window_left
+ webkit_dom_dom_window_get_inner_width (dom_window
);
13779 element_left
= webkit_dom_element_get_offset_left (selection_start_marker
);
13780 element_top
= webkit_dom_element_get_offset_top (selection_start_marker
);
13782 /* Check if caret is inside viewport, if not move to it */
13783 if (!(element_top
>= window_top
&& element_top
<= window_bottom
&&
13784 element_left
>= window_left
&& element_left
<= window_right
)) {
13785 webkit_dom_element_scroll_into_view (selection_start_marker
, TRUE
);
13788 e_editor_dom_selection_restore (editor_page
);
13790 g_clear_object (&dom_window
);
13794 mark_and_remove_trailing_space (WebKitDOMDocument
*document
,
13795 WebKitDOMNode
*node
)
13797 WebKitDOMElement
*element
;
13799 element
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
13800 webkit_dom_element_set_attribute (element
, "data-hidden-space", "", NULL
);
13801 webkit_dom_node_insert_before (
13802 webkit_dom_node_get_parent_node (node
),
13803 WEBKIT_DOM_NODE (element
),
13804 webkit_dom_node_get_next_sibling (node
),
13806 webkit_dom_character_data_replace_data (
13807 WEBKIT_DOM_CHARACTER_DATA (node
),
13808 webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
)),
13815 mark_and_remove_leading_space (WebKitDOMDocument
*document
,
13816 WebKitDOMNode
*node
)
13818 WebKitDOMElement
*element
;
13820 element
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
13821 webkit_dom_element_set_attribute (element
, "data-hidden-space", "", NULL
);
13822 webkit_dom_node_insert_before (
13823 webkit_dom_node_get_parent_node (node
),
13824 WEBKIT_DOM_NODE (element
),
13827 webkit_dom_character_data_replace_data (
13828 WEBKIT_DOM_CHARACTER_DATA (node
), 0, 1, "", NULL
);
13831 static WebKitDOMElement
*
13832 wrap_lines (EEditorPage
*editor_page
,
13833 WebKitDOMNode
*block
,
13834 gboolean remove_all_br
,
13835 gint length_to_wrap
,
13836 gint word_wrap_length
)
13838 WebKitDOMDocument
*document
;
13839 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
13840 WebKitDOMNode
*node
, *start_node
, *block_clone
= NULL
;
13841 WebKitDOMNode
*start_point
= NULL
, *first_child
, *last_child
;
13843 gulong length_left
;
13844 gchar
*text_content
;
13845 gboolean compensated
= FALSE
;
13846 gboolean check_next_node
= FALSE
;
13848 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
13850 document
= e_editor_page_get_document (editor_page
);
13852 if (!webkit_dom_node_has_child_nodes (block
))
13853 return WEBKIT_DOM_ELEMENT (block
);
13855 /* Avoid wrapping when the block contains just the BR element alone
13856 * or with selection markers. */
13857 if ((first_child
= webkit_dom_node_get_first_child (block
)) &&
13858 WEBKIT_DOM_IS_HTML_BR_ELEMENT (first_child
)) {
13859 WebKitDOMNode
*next_sibling
;
13861 if ((next_sibling
= webkit_dom_node_get_next_sibling (first_child
))) {
13862 if (e_editor_dom_is_selection_position_node (next_sibling
) &&
13863 (next_sibling
= webkit_dom_node_get_next_sibling (next_sibling
)) &&
13864 e_editor_dom_is_selection_position_node (next_sibling
) &&
13865 !webkit_dom_node_get_next_sibling (next_sibling
))
13866 return WEBKIT_DOM_ELEMENT (block
);
13868 return WEBKIT_DOM_ELEMENT (block
);
13871 block_clone
= webkit_dom_node_clone_node_with_error (block
, TRUE
, NULL
);
13873 /* When we wrap, we are wrapping just the text after caret, text
13874 * before the caret is already wrapped, so unwrap the text after
13875 * the caret position */
13876 selection_end_marker
= webkit_dom_element_query_selector (
13877 WEBKIT_DOM_ELEMENT (block_clone
),
13878 "span#-x-evo-selection-end-marker",
13881 if (selection_end_marker
) {
13882 WebKitDOMNode
*nd
= WEBKIT_DOM_NODE (selection_end_marker
);
13885 WebKitDOMNode
*parent_node
;
13886 WebKitDOMNode
*next_nd
= webkit_dom_node_get_next_sibling (nd
);
13888 parent_node
= webkit_dom_node_get_parent_node (nd
);
13889 if (!next_nd
&& parent_node
&& !webkit_dom_node_is_same_node (parent_node
, block_clone
))
13890 next_nd
= webkit_dom_node_get_next_sibling (parent_node
);
13892 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd
)) {
13895 else if (element_has_class (WEBKIT_DOM_ELEMENT (nd
), "-x-evo-wrap-br"))
13897 } else if (WEBKIT_DOM_IS_ELEMENT (nd
) &&
13898 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd
), "data-hidden-space"))
13899 webkit_dom_html_element_set_outer_text (
13900 WEBKIT_DOM_HTML_ELEMENT (nd
), " ", NULL
);
13906 WebKitDOMNodeList
*list
= NULL
;
13908 list
= webkit_dom_element_query_selector_all (
13909 WEBKIT_DOM_ELEMENT (block_clone
), "span[data-hidden-space]", NULL
);
13910 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
13911 WebKitDOMNode
*hidden_space_node
;
13913 hidden_space_node
= webkit_dom_node_list_item (list
, ii
);
13914 webkit_dom_html_element_set_outer_text (
13915 WEBKIT_DOM_HTML_ELEMENT (hidden_space_node
), " ", NULL
);
13917 g_clear_object (&list
);
13920 /* We have to start from the end of the last wrapped line */
13921 selection_start_marker
= webkit_dom_element_query_selector (
13922 WEBKIT_DOM_ELEMENT (block_clone
),
13923 "span#-x-evo-selection-start-marker",
13926 if (selection_start_marker
) {
13927 gboolean first_removed
= FALSE
;
13930 nd
= webkit_dom_node_get_previous_sibling (
13931 WEBKIT_DOM_NODE (selection_start_marker
));
13933 WebKitDOMNode
*prev_nd
= webkit_dom_node_get_previous_sibling (nd
);
13935 if (!prev_nd
&& !webkit_dom_node_is_same_node (webkit_dom_node_get_parent_node (nd
), block_clone
))
13936 prev_nd
= webkit_dom_node_get_previous_sibling (webkit_dom_node_get_parent_node (nd
));
13938 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd
)) {
13939 if (first_removed
) {
13944 first_removed
= TRUE
;
13946 } else if (WEBKIT_DOM_IS_ELEMENT (nd
) &&
13947 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd
), "data-hidden-space")) {
13948 webkit_dom_html_element_set_outer_text (
13949 WEBKIT_DOM_HTML_ELEMENT (nd
), " ", NULL
);
13950 } else if (!prev_nd
) {
13951 WebKitDOMNode
*parent
;
13953 parent
= webkit_dom_node_get_parent_node (nd
);
13954 if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
))
13962 webkit_dom_node_normalize (block_clone
);
13963 node
= webkit_dom_node_get_first_child (block_clone
);
13965 text_content
= webkit_dom_node_get_text_content (node
);
13966 if (g_strcmp0 ("\n", text_content
) == 0)
13967 node
= webkit_dom_node_get_next_sibling (node
);
13968 g_free (text_content
);
13972 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (start_point
))
13973 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_point
));
13975 node
= start_point
;
13976 start_node
= block_clone
;
13983 WebKitDOMElement
*element
;
13985 if (WEBKIT_DOM_IS_TEXT (node
)) {
13986 const gchar
*newline
;
13987 WebKitDOMNode
*next_sibling
;
13989 /* If there is temporary hidden space we remove it */
13990 text_content
= webkit_dom_node_get_text_content (node
);
13991 if (strstr (text_content
, UNICODE_ZERO_WIDTH_SPACE
)) {
13992 if (g_str_has_prefix (text_content
, UNICODE_ZERO_WIDTH_SPACE
))
13993 webkit_dom_character_data_delete_data (
13994 WEBKIT_DOM_CHARACTER_DATA (node
),
13998 if (g_str_has_suffix (text_content
, UNICODE_ZERO_WIDTH_SPACE
))
13999 webkit_dom_character_data_delete_data (
14000 WEBKIT_DOM_CHARACTER_DATA (node
),
14001 g_utf8_strlen (text_content
, -1) - 1,
14004 g_free (text_content
);
14005 text_content
= webkit_dom_node_get_text_content (node
);
14007 newline
= strstr (text_content
, "\n");
14009 next_sibling
= node
;
14011 next_sibling
= WEBKIT_DOM_NODE (webkit_dom_text_split_text (
14012 WEBKIT_DOM_TEXT (next_sibling
),
14013 g_utf8_pointer_to_offset (text_content
, newline
),
14019 element
= webkit_dom_document_create_element (
14020 document
, "BR", NULL
);
14021 element_add_class (element
, "-x-evo-wrap-br");
14023 webkit_dom_node_insert_before (
14024 webkit_dom_node_get_parent_node (next_sibling
),
14025 WEBKIT_DOM_NODE (element
),
14029 g_free (text_content
);
14031 text_content
= webkit_dom_node_get_text_content (next_sibling
);
14032 if (g_str_has_prefix (text_content
, "\n")) {
14033 webkit_dom_character_data_delete_data (
14034 WEBKIT_DOM_CHARACTER_DATA (next_sibling
), 0, 1, NULL
);
14035 g_free (text_content
);
14037 webkit_dom_node_get_text_content (next_sibling
);
14039 newline
= strstr (text_content
, "\n");
14041 g_free (text_content
);
14042 } else if (WEBKIT_DOM_IS_ELEMENT (node
)) {
14043 if (e_editor_dom_is_selection_position_node (node
)) {
14044 if (line_length
== 0) {
14045 WebKitDOMNode
*tmp_node
;
14047 tmp_node
= webkit_dom_node_get_previous_sibling (node
);
14048 /* Only check if there is some node before the selection marker. */
14049 if (tmp_node
&& !e_editor_dom_is_selection_position_node (tmp_node
))
14050 check_next_node
= TRUE
;
14052 node
= webkit_dom_node_get_next_sibling (node
);
14056 check_next_node
= FALSE
;
14057 /* If element is ANCHOR we wrap it separately */
14058 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
)) {
14059 glong anchor_length
;
14060 WebKitDOMNode
*next_sibling
;
14062 text_content
= webkit_dom_node_get_text_content (node
);
14063 anchor_length
= g_utf8_strlen (text_content
, -1);
14064 g_free (text_content
);
14066 next_sibling
= webkit_dom_node_get_next_sibling (node
);
14067 /* If the anchor doesn't fit on the line, add it to a separate line. */
14068 if ((line_length
+ anchor_length
) > length_to_wrap
) {
14069 /* Put <BR> before the anchor, thus it starts on a new line */
14070 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14071 element_add_class (element
, "-x-evo-wrap-br");
14072 webkit_dom_node_insert_before (
14073 webkit_dom_node_get_parent_node (node
),
14074 WEBKIT_DOM_NODE (element
),
14078 /* When the anchor itself is too long */
14079 if (anchor_length
>= length_to_wrap
) {
14080 /* Put <BR> after the anchor, thus it doesn't contain text after it */
14081 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14082 element_add_class (element
, "-x-evo-wrap-br");
14083 webkit_dom_node_insert_before (
14084 webkit_dom_node_get_parent_node (node
),
14085 WEBKIT_DOM_NODE (element
),
14091 line_length
= anchor_length
;
14094 line_length
+= anchor_length
;
14097 node
= next_sibling
;
14101 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "Apple-tab-span")) {
14102 WebKitDOMNode
*sibling
;
14105 sibling
= webkit_dom_node_get_previous_sibling (node
);
14106 if (sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
14107 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "Apple-tab-span"))
14108 tab_length
= TAB_LENGTH
;
14110 tab_length
= TAB_LENGTH
- (line_length
+ (compensated
? 0 : (word_wrap_length
- length_to_wrap
))) % TAB_LENGTH
;
14111 compensated
= TRUE
;
14114 if (line_length
+ tab_length
> length_to_wrap
) {
14115 if (webkit_dom_node_get_next_sibling (node
)) {
14116 element
= webkit_dom_document_create_element (
14117 document
, "BR", NULL
);
14118 element_add_class (element
, "-x-evo-wrap-br");
14119 node
= webkit_dom_node_insert_before (
14120 webkit_dom_node_get_parent_node (node
),
14121 WEBKIT_DOM_NODE (element
),
14122 webkit_dom_node_get_next_sibling (node
),
14126 compensated
= FALSE
;
14128 line_length
+= tab_length
;
14130 sibling
= webkit_dom_node_get_next_sibling (node
);
14134 /* When we are not removing user-entered BR elements (lines wrapped by user),
14135 * we need to skip those elements */
14136 if (!remove_all_br
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
14137 if (!element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-wrap-br")) {
14139 compensated
= FALSE
;
14140 node
= webkit_dom_node_get_next_sibling (node
);
14146 WebKitDOMNode
*sibling
;
14148 sibling
= webkit_dom_node_get_next_sibling (node
);
14153 /* If length of this node + what we already have is still less
14154 * then length_to_wrap characters, then just concatenate it and
14155 * continue to next node */
14156 length_left
= webkit_dom_character_data_get_length (
14157 WEBKIT_DOM_CHARACTER_DATA (node
));
14159 if ((length_left
+ line_length
) <= length_to_wrap
) {
14160 if (check_next_node
)
14162 line_length
+= length_left
;
14163 if (line_length
== length_to_wrap
) {
14166 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14167 element_add_class (element
, "-x-evo-wrap-br");
14169 webkit_dom_node_insert_before (
14170 webkit_dom_node_get_parent_node (node
),
14171 WEBKIT_DOM_NODE (element
),
14172 webkit_dom_node_get_next_sibling (node
),
14178 /* wrap until we have something */
14179 while (node
&& (length_left
+ line_length
) > length_to_wrap
) {
14180 gboolean insert_and_continue
;
14184 insert_and_continue
= FALSE
;
14186 if (!WEBKIT_DOM_IS_CHARACTER_DATA (node
))
14189 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14190 element_add_class (element
, "-x-evo-wrap-br");
14192 max_length
= length_to_wrap
- line_length
;
14193 if (max_length
< 0)
14194 max_length
= length_to_wrap
;
14195 else if (max_length
== 0) {
14196 if (check_next_node
) {
14197 insert_and_continue
= TRUE
;
14201 /* Break before the current node and continue. */
14202 webkit_dom_node_insert_before (
14203 webkit_dom_node_get_parent_node (node
),
14204 WEBKIT_DOM_NODE (element
),
14211 /* Allow anchors to break on any character. */
14212 if (g_object_steal_data (G_OBJECT (node
), "-x-evo-anchor-text"))
14213 offset
= max_length
;
14215 /* Find where we can line-break the node so that it
14216 * effectively fills the rest of current row. */
14217 offset
= find_where_to_break_line (
14218 WEBKIT_DOM_CHARACTER_DATA (node
), max_length
);
14220 /* When pressing delete on the end of line to concatenate
14221 * the last word from the line and first word from the
14222 * next line we will end with the second word split
14223 * somewhere in the middle (to be precise it will be
14224 * split after the last character that will fit on the
14225 * previous line. To avoid that we need to put the
14226 * concatenated word on the next line. */
14227 if (offset
== -1 || check_next_node
) {
14228 WebKitDOMNode
*prev_sibling
;
14231 check_next_node
= FALSE
;
14232 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
14233 if (prev_sibling
&& e_editor_dom_is_selection_position_node (prev_sibling
)) {
14234 WebKitDOMNode
*prev_br
= NULL
;
14236 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14238 /* Collapsed selection */
14239 if (prev_sibling
&& e_editor_dom_is_selection_position_node (prev_sibling
))
14240 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14242 if (prev_sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
) &&
14243 element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-wrap-br")) {
14244 prev_br
= prev_sibling
;
14245 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14248 if (prev_sibling
&& WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling
)) {
14250 glong text_length
, length
= 0;
14252 data
= webkit_dom_character_data_get_data (
14253 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
14254 text_length
= webkit_dom_character_data_get_length (
14255 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
14257 /* Find the last character where we can break. */
14258 while (text_length
- length
> 0) {
14259 if (strchr (" ", data
[text_length
- length
- 1])) {
14262 } else if (data
[text_length
- length
- 1] == '-' &&
14263 text_length
- length
> 1 &&
14264 !strchr (" ", data
[text_length
- length
- 2]))
14269 if (text_length
!= length
) {
14272 webkit_dom_text_split_text (
14273 WEBKIT_DOM_TEXT (prev_sibling
),
14274 text_length
- length
,
14277 if ((nd
= webkit_dom_node_get_next_sibling (prev_sibling
))) {
14280 while (nd_content
= webkit_dom_node_get_text_content (nd
), nd_content
) {
14281 gboolean changed
= FALSE
;
14284 if (*nd_content
== ' ') {
14285 mark_and_remove_leading_space (document
, nd
);
14289 if (!webkit_dom_node_get_next_sibling (nd
) &&
14290 g_str_has_suffix (nd_content
, " ")) {
14291 mark_and_remove_trailing_space (document
, nd
);
14296 g_free (nd_content
);
14304 remove_node (prev_br
);
14305 webkit_dom_node_insert_before (
14306 webkit_dom_node_get_parent_node (nd
),
14307 WEBKIT_DOM_NODE (element
),
14312 line_length
= length
;
14319 if (insert_and_continue
) {
14320 webkit_dom_node_insert_before (
14321 webkit_dom_node_get_parent_node (node
),
14322 WEBKIT_DOM_NODE (element
),
14328 insert_and_continue
= FALSE
;
14332 offset
= offset
!= -1 ? offset
: max_length
;
14339 if (offset
!= length_left
&& offset
!= 0) {
14340 webkit_dom_text_split_text (
14341 WEBKIT_DOM_TEXT (node
), offset
, NULL
);
14343 nd
= webkit_dom_node_get_next_sibling (node
);
14348 gboolean no_sibling
= FALSE
;
14351 while (nd_content
= webkit_dom_node_get_text_content (nd
), nd_content
) {
14352 gboolean changed
= FALSE
;
14355 if (*nd_content
== ' ') {
14356 mark_and_remove_leading_space (document
, nd
);
14360 if (!webkit_dom_node_get_next_sibling (nd
) &&
14361 length_left
<= length_to_wrap
&&
14362 g_str_has_suffix (nd_content
, " ")) {
14363 mark_and_remove_trailing_space (document
, nd
);
14369 g_free (nd_content
);
14376 webkit_dom_node_insert_before (
14377 webkit_dom_node_get_parent_node (node
),
14378 WEBKIT_DOM_NODE (element
),
14384 nd_content
= webkit_dom_node_get_text_content (nd
);
14387 g_free (nd_content
);
14394 node
= webkit_dom_node_get_next_sibling (
14395 WEBKIT_DOM_NODE (element
));
14398 node
= webkit_dom_node_get_next_sibling (node
);
14403 webkit_dom_node_append_child (
14404 webkit_dom_node_get_parent_node (node
),
14405 WEBKIT_DOM_NODE (element
),
14409 if (node
&& WEBKIT_DOM_IS_CHARACTER_DATA (node
))
14410 length_left
= webkit_dom_character_data_get_length (
14411 WEBKIT_DOM_CHARACTER_DATA (node
));
14414 compensated
= FALSE
;
14416 line_length
+= length_left
- offset
;
14421 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node
)) {
14423 compensated
= FALSE
;
14426 /* Move to next node */
14427 if (webkit_dom_node_has_child_nodes (node
)) {
14428 node
= webkit_dom_node_get_first_child (node
);
14429 } else if (webkit_dom_node_get_next_sibling (node
)) {
14430 node
= webkit_dom_node_get_next_sibling (node
);
14432 WebKitDOMNode
*tmp_parent
;
14434 if (webkit_dom_node_is_equal_node (node
, start_node
))
14437 /* Find a next node that we can process. */
14438 tmp_parent
= webkit_dom_node_get_parent_node (node
);
14439 if (tmp_parent
&& webkit_dom_node_get_next_sibling (tmp_parent
))
14440 node
= webkit_dom_node_get_next_sibling (tmp_parent
);
14442 WebKitDOMNode
*tmp
;
14445 /* Find a node that is not a start node (that would mean
14446 * that we already processed the whole block) and it has
14447 * a sibling that we can process. */
14448 while (tmp
&& !webkit_dom_node_is_equal_node (tmp
, start_node
) &&
14449 !webkit_dom_node_get_next_sibling (tmp
)) {
14450 tmp
= webkit_dom_node_get_parent_node (tmp
);
14453 /* If we found a node to process, let's process its
14454 * sibling, otherwise give up. */
14456 node
= webkit_dom_node_get_next_sibling (tmp
);
14463 last_child
= webkit_dom_node_get_last_child (block_clone
);
14464 if (last_child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child
) &&
14465 element_has_class (WEBKIT_DOM_ELEMENT (last_child
), "-x-evo-wrap-br"))
14466 remove_node (last_child
);
14468 webkit_dom_node_normalize (block_clone
);
14470 node
= webkit_dom_node_get_parent_node (block
);
14472 /* Replace block with wrapped one */
14473 webkit_dom_node_replace_child (
14474 node
, block_clone
, block
, NULL
);
14477 return WEBKIT_DOM_ELEMENT (block_clone
);
14481 e_editor_dom_remove_wrapping_from_element (WebKitDOMElement
*element
)
14483 WebKitDOMNodeList
*list
= NULL
;
14486 g_return_if_fail (element
!= NULL
);
14488 list
= webkit_dom_element_query_selector_all (
14489 element
, "br.-x-evo-wrap-br", NULL
);
14490 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14491 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
14492 WebKitDOMNode
*parent
;
14494 parent
= e_editor_dom_get_parent_block_node_from_child (node
);
14495 if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "data-user-wrapped"))
14496 remove_node (node
);
14499 g_clear_object (&list
);
14501 list
= webkit_dom_element_query_selector_all (
14502 element
, "span[data-hidden-space]", NULL
);
14503 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14504 WebKitDOMNode
*hidden_space_node
;
14505 WebKitDOMNode
*parent
;
14507 hidden_space_node
= webkit_dom_node_list_item (list
, ii
);
14508 parent
= e_editor_dom_get_parent_block_node_from_child (hidden_space_node
);
14509 if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "data-user-wrapped")) {
14510 webkit_dom_html_element_set_outer_text (
14511 WEBKIT_DOM_HTML_ELEMENT (hidden_space_node
), " ", NULL
);
14514 g_clear_object (&list
);
14516 webkit_dom_node_normalize (WEBKIT_DOM_NODE (element
));
14520 e_editor_dom_remove_quoting_from_element (WebKitDOMElement
*element
)
14523 WebKitDOMHTMLCollection
*collection
= NULL
;
14525 g_return_if_fail (element
!= NULL
);
14527 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
14528 element
, "-x-evo-quoted");
14529 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;)
14530 remove_node (webkit_dom_html_collection_item (collection
, ii
));
14531 g_clear_object (&collection
);
14533 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
14534 element
, "-x-evo-temp-br");
14535 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;)
14536 remove_node (webkit_dom_html_collection_item (collection
, ii
));
14537 g_clear_object (&collection
);
14539 webkit_dom_node_normalize (WEBKIT_DOM_NODE (element
));
14543 e_editor_dom_get_paragraph_element (EEditorPage
*editor_page
,
14547 WebKitDOMDocument
*document
;
14548 WebKitDOMElement
*element
;
14550 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14552 document
= e_editor_page_get_document (editor_page
);
14553 element
= webkit_dom_document_create_element (document
, "DIV", NULL
);
14554 e_editor_dom_set_paragraph_style (editor_page
, element
, width
, offset
, NULL
);
14560 e_editor_dom_put_node_into_paragraph (EEditorPage
*editor_page
,
14561 WebKitDOMNode
*node
,
14562 gboolean with_input
)
14564 WebKitDOMDocument
*document
;
14565 WebKitDOMRange
*range
= NULL
;
14566 WebKitDOMElement
*container
;
14568 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14570 document
= e_editor_page_get_document (editor_page
);
14571 range
= webkit_dom_document_create_range (document
);
14572 container
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
14573 webkit_dom_range_select_node (range
, node
, NULL
);
14574 webkit_dom_range_surround_contents (range
, WEBKIT_DOM_NODE (container
), NULL
);
14575 /* We have to move caret position inside this container */
14577 dom_add_selection_markers_into_element_end (document
, container
, NULL
, NULL
);
14579 g_clear_object (&range
);
14585 e_editor_dom_wrap_paragraph_length (EEditorPage
*editor_page
,
14586 WebKitDOMElement
*paragraph
,
14589 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14590 g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph
), NULL
);
14591 g_return_val_if_fail (length
>= MINIMAL_PARAGRAPH_WIDTH
, NULL
);
14593 return wrap_lines (editor_page
, WEBKIT_DOM_NODE (paragraph
), FALSE
, length
,
14594 e_editor_page_get_word_wrap_length (editor_page
));
14598 * e_html_editor_selection_wrap_lines:
14599 * @selection: an #EEditorSelection
14601 * Wraps all lines in current selection to be 71 characters long.
14605 e_editor_dom_selection_wrap (EEditorPage
*editor_page
)
14607 WebKitDOMDocument
*document
;
14608 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
14609 WebKitDOMNode
*block
, *next_block
;
14610 EEditorHistoryEvent
*ev
= NULL
;
14611 EEditorUndoRedoManager
*manager
;
14612 gboolean after_selection_end
= FALSE
, html_mode
;
14613 gint word_wrap_length
;
14615 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
14617 document
= e_editor_page_get_document (editor_page
);
14618 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14620 e_editor_dom_selection_save (editor_page
);
14621 selection_start_marker
= webkit_dom_document_get_element_by_id (
14622 document
, "-x-evo-selection-start-marker");
14623 selection_end_marker
= webkit_dom_document_get_element_by_id (
14624 document
, "-x-evo-selection-end-marker");
14626 /* If the selection was not saved, move it into the first child of body */
14627 if (!selection_start_marker
|| !selection_end_marker
) {
14628 WebKitDOMHTMLElement
*body
;
14629 WebKitDOMNode
*child
;
14631 body
= webkit_dom_document_get_body (document
);
14632 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
14634 dom_add_selection_markers_into_element_start (
14636 WEBKIT_DOM_ELEMENT (child
),
14637 &selection_start_marker
,
14638 &selection_end_marker
);
14641 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
14642 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
14643 ev
= g_new0 (EEditorHistoryEvent
, 1);
14644 ev
->type
= HISTORY_WRAP
;
14646 e_editor_dom_selection_get_coordinates (editor_page
,
14647 &ev
->before
.start
.x
,
14648 &ev
->before
.start
.y
,
14650 &ev
->before
.end
.y
);
14652 ev
->data
.style
.from
= 1;
14653 ev
->data
.style
.to
= 1;
14656 block
= e_editor_dom_get_parent_block_node_from_child (
14657 WEBKIT_DOM_NODE (selection_start_marker
));
14659 html_mode
= e_editor_page_get_html_mode (editor_page
);
14661 /* Process all blocks that are in the selection one by one */
14662 while (block
&& !after_selection_end
) {
14663 gboolean quoted
= FALSE
;
14664 gint citation_level
, quote
;
14665 WebKitDOMElement
*wrapped_paragraph
;
14667 next_block
= webkit_dom_node_get_next_sibling (block
);
14669 /* Don't try to wrap the 'Normal' blocks as they are already wrapped and*/
14670 /* also skip blocks that we already wrapped with this function. */
14671 if ((!html_mode
&& webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph")) ||
14672 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-user-wrapped")) {
14673 block
= next_block
;
14677 if (webkit_dom_element_query_selector (
14678 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
14680 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
14684 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
14686 after_selection_end
= webkit_dom_node_contains (
14687 block
, WEBKIT_DOM_NODE (selection_end_marker
));
14689 citation_level
= e_editor_dom_get_citation_level (block
);
14690 quote
= citation_level
? citation_level
* 2 : 0;
14692 wrapped_paragraph
= e_editor_dom_wrap_paragraph_length (
14693 editor_page
, WEBKIT_DOM_ELEMENT (block
), word_wrap_length
- quote
);
14695 webkit_dom_element_set_attribute (
14696 wrapped_paragraph
, "data-user-wrapped", "", NULL
);
14698 if (quoted
&& !html_mode
)
14699 e_editor_dom_quote_plain_text_element_after_wrapping (
14700 editor_page
, wrapped_paragraph
, citation_level
);
14702 block
= next_block
;
14706 e_editor_dom_selection_get_coordinates (editor_page
,
14707 &ev
->after
.start
.x
,
14708 &ev
->after
.start
.y
,
14711 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
14714 e_editor_dom_selection_restore (editor_page
);
14716 e_editor_dom_force_spell_check_in_viewport (editor_page
);
14718 e_editor_page_emit_content_changed (editor_page
);
14722 e_editor_dom_wrap_paragraphs_in_document (EEditorPage
*editor_page
)
14724 WebKitDOMDocument
*document
;
14725 WebKitDOMNodeList
*list
= NULL
;
14728 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
14730 document
= e_editor_page_get_document (editor_page
);
14731 list
= webkit_dom_document_query_selector_all (
14732 document
, "[data-evo-paragraph]:not(#-x-evo-input-start)", NULL
);
14734 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14735 gint word_wrap_length
, quote
, citation_level
;
14736 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
14738 citation_level
= e_editor_dom_get_citation_level (node
);
14739 quote
= citation_level
? citation_level
* 2 : 0;
14740 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14742 if (node_is_list (node
)) {
14743 WebKitDOMNode
*item
= webkit_dom_node_get_first_child (node
);
14745 while (item
&& WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
14746 e_editor_dom_wrap_paragraph_length (
14747 editor_page
, WEBKIT_DOM_ELEMENT (item
), word_wrap_length
- quote
);
14748 item
= webkit_dom_node_get_next_sibling (item
);
14751 e_editor_dom_wrap_paragraph_length (
14752 editor_page
, WEBKIT_DOM_ELEMENT (node
), word_wrap_length
- quote
);
14755 g_clear_object (&list
);
14759 e_editor_dom_wrap_paragraph (EEditorPage
*editor_page
,
14760 WebKitDOMElement
*paragraph
)
14762 gint indentation_level
, citation_level
, quote
;
14763 gint word_wrap_length
, final_width
, offset
= 0;
14765 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14766 g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph
), NULL
);
14768 indentation_level
= get_indentation_level (paragraph
);
14769 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (paragraph
));
14771 if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph
))) {
14772 gint list_level
= get_list_level (WEBKIT_DOM_NODE (paragraph
));
14773 indentation_level
= 0;
14775 if (list_level
> 0)
14776 offset
= list_level
* -SPACES_PER_LIST_LEVEL
;
14778 offset
= -SPACES_PER_LIST_LEVEL
;
14781 quote
= citation_level
? citation_level
* 2 : 0;
14783 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14784 final_width
= word_wrap_length
- quote
+ offset
;
14785 final_width
-= SPACES_PER_INDENTATION
* indentation_level
;
14787 return e_editor_dom_wrap_paragraph_length (
14788 editor_page
, WEBKIT_DOM_ELEMENT (paragraph
), final_width
);
14792 get_has_style (EEditorPage
*editor_page
,
14793 const gchar
*style_tag
)
14795 WebKitDOMNode
*node
;
14796 WebKitDOMElement
*element
;
14797 WebKitDOMRange
*range
= NULL
;
14801 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
14803 range
= e_editor_dom_get_current_range (editor_page
);
14807 node
= webkit_dom_range_get_start_container (range
, NULL
);
14808 if (WEBKIT_DOM_IS_ELEMENT (node
))
14809 element
= WEBKIT_DOM_ELEMENT (node
);
14811 element
= webkit_dom_node_get_parent_element (node
);
14812 g_clear_object (&range
);
14814 tag_len
= strlen (style_tag
);
14816 while (!result
&& element
) {
14817 gchar
*element_tag
;
14818 gboolean accept_citation
= FALSE
;
14820 element_tag
= webkit_dom_element_get_tag_name (element
);
14822 if (g_ascii_strncasecmp (style_tag
, "citation", 8) == 0) {
14823 accept_citation
= TRUE
;
14824 result
= WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element
);
14825 if (element_has_class (element
, "-x-evo-indented"))
14828 result
= ((tag_len
== strlen (element_tag
)) &&
14829 (g_ascii_strncasecmp (element_tag
, style_tag
, tag_len
) == 0));
14832 /* Special case: <blockquote type=cite> marks quotation, while
14833 * just <blockquote> is used for indentation. If the <blockquote>
14834 * has type=cite, then ignore it unless style_tag is "citation" */
14835 if (result
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element
)) {
14836 if (webkit_dom_element_has_attribute (element
, "type")) {
14837 gchar
*type
= webkit_dom_element_get_attribute (element
, "type");
14838 if (!accept_citation
&& (type
&& g_ascii_strncasecmp (type
, "cite", 4) == 0)) {
14843 if (accept_citation
)
14848 g_free (element_tag
);
14853 element
= webkit_dom_node_get_parent_element (
14854 WEBKIT_DOM_NODE (element
));
14860 typedef gboolean (*IsRightFormatNodeFunc
) (WebKitDOMElement
*element
);
14863 dom_selection_is_font_format (EEditorPage
*editor_page
,
14864 IsRightFormatNodeFunc func
,
14865 gboolean
*previous_value
)
14867 WebKitDOMDocument
*document
;
14868 WebKitDOMDOMWindow
*dom_window
= NULL
;
14869 WebKitDOMDOMSelection
*dom_selection
= NULL
;
14870 WebKitDOMNode
*start
, *end
, *sibling
;
14871 WebKitDOMRange
*range
= NULL
;
14872 gboolean ret_val
= FALSE
;
14874 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
14876 if (!e_editor_page_get_html_mode (editor_page
))
14879 document
= e_editor_page_get_document (editor_page
);
14880 dom_window
= webkit_dom_document_get_default_view (document
);
14881 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
14882 g_clear_object (&dom_window
);
14884 if (!webkit_dom_dom_selection_get_range_count (dom_selection
))
14887 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
14891 if (webkit_dom_range_get_collapsed (range
, NULL
) && previous_value
) {
14892 WebKitDOMNode
*node
;
14893 gchar
* text_content
;
14895 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
14896 /* If we are changing the format of block we have to re-set the
14897 * format property, otherwise it will be turned off because of no
14898 * text in block. */
14899 text_content
= webkit_dom_node_get_text_content (node
);
14900 if (g_strcmp0 (text_content
, "") == 0) {
14901 g_free (text_content
);
14902 ret_val
= *previous_value
;
14905 g_free (text_content
);
14908 /* Range without start or end point is a wrong range. */
14909 start
= webkit_dom_range_get_start_container (range
, NULL
);
14910 end
= webkit_dom_range_get_end_container (range
, NULL
);
14911 if (!start
|| !end
)
14914 if (WEBKIT_DOM_IS_TEXT (start
))
14915 start
= webkit_dom_node_get_parent_node (start
);
14916 while (start
&& WEBKIT_DOM_IS_ELEMENT (start
) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start
)) {
14917 /* Find the start point's parent node with given formatting. */
14918 if (func (WEBKIT_DOM_ELEMENT (start
))) {
14922 start
= webkit_dom_node_get_parent_node (start
);
14925 /* Start point doesn't have the given formatting. */
14929 /* If the selection is collapsed, we can return early. */
14930 if (webkit_dom_range_get_collapsed (range
, NULL
))
14933 /* The selection is in the same node and that node is supposed to have
14934 * the same formatting (otherwise it is split up with formatting element. */
14935 if (webkit_dom_node_is_same_node (
14936 webkit_dom_range_get_start_container (range
, NULL
),
14937 webkit_dom_range_get_end_container (range
, NULL
)))
14942 if (WEBKIT_DOM_IS_TEXT (end
))
14943 end
= webkit_dom_node_get_parent_node (end
);
14944 while (end
&& WEBKIT_DOM_IS_ELEMENT (end
) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end
)) {
14945 /* Find the end point's parent node with given formatting. */
14946 if (func (WEBKIT_DOM_ELEMENT (end
))) {
14950 end
= webkit_dom_node_get_parent_node (end
);
14958 /* Now go between the end points and check the inner nodes for format validity. */
14960 while ((sibling
= webkit_dom_node_get_next_sibling (sibling
))) {
14961 if (webkit_dom_node_is_same_node (sibling
, end
)) {
14966 if (WEBKIT_DOM_IS_TEXT (sibling
))
14968 else if (func (WEBKIT_DOM_ELEMENT (sibling
)))
14970 else if (webkit_dom_node_get_first_child (sibling
)) {
14971 WebKitDOMNode
*first_child
;
14973 first_child
= webkit_dom_node_get_first_child (sibling
);
14974 if (!webkit_dom_node_get_next_sibling (first_child
))
14975 if (WEBKIT_DOM_IS_ELEMENT (first_child
) && func (WEBKIT_DOM_ELEMENT (first_child
)))
14986 while ((sibling
= webkit_dom_node_get_previous_sibling (sibling
))) {
14987 if (webkit_dom_node_is_same_node (sibling
, start
))
14990 if (WEBKIT_DOM_IS_TEXT (sibling
))
14992 else if (func (WEBKIT_DOM_ELEMENT (sibling
)))
14994 else if (webkit_dom_node_get_first_child (sibling
)) {
14995 WebKitDOMNode
*first_child
;
14997 first_child
= webkit_dom_node_get_first_child (sibling
);
14998 if (!webkit_dom_node_get_next_sibling (first_child
))
14999 if (WEBKIT_DOM_IS_ELEMENT (first_child
) && func (WEBKIT_DOM_ELEMENT (first_child
)))
15011 g_clear_object (&range
);
15012 g_clear_object (&dom_selection
);
15018 is_underline_element (WebKitDOMElement
*element
)
15020 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15023 return element_has_tag (element
, "u");
15027 * e_html_editor_selection_is_underline:
15028 * @selection: an #EEditorSelection
15030 * Returns whether current selection or letter at current cursor position
15033 * Returns @TRUE when selection is underlined, @FALSE otherwise.
15036 e_editor_dom_selection_is_underline (EEditorPage
*editor_page
)
15038 gboolean is_underline
;
15040 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15042 is_underline
= e_editor_page_get_underline (editor_page
);
15043 is_underline
= dom_selection_is_font_format (
15044 editor_page
, (IsRightFormatNodeFunc
) is_underline_element
, &is_underline
);
15046 return is_underline
;
15049 static WebKitDOMElement
*
15050 set_font_style (WebKitDOMDocument
*document
,
15051 const gchar
*element_name
,
15054 WebKitDOMElement
*element
;
15055 WebKitDOMNode
*parent
, *clone
= NULL
;
15057 element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-end-marker");
15058 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
15060 WebKitDOMNode
*node
;
15061 WebKitDOMElement
*el
;
15064 el
= webkit_dom_document_create_element (document
, element_name
, NULL
);
15065 webkit_dom_html_element_set_inner_text (
15066 WEBKIT_DOM_HTML_ELEMENT (el
), UNICODE_ZERO_WIDTH_SPACE
, NULL
);
15068 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
15069 webkit_dom_node_append_child (
15070 WEBKIT_DOM_NODE (el
), node
, NULL
);
15071 name
= webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (parent
));
15072 if (g_ascii_strcasecmp (name
, element_name
) == 0 && g_ascii_strcasecmp (name
, "font") != 0)
15073 webkit_dom_node_insert_before (
15074 webkit_dom_node_get_parent_node (parent
),
15075 WEBKIT_DOM_NODE (el
),
15076 webkit_dom_node_get_next_sibling (parent
),
15079 webkit_dom_node_insert_before (
15081 WEBKIT_DOM_NODE (el
),
15082 WEBKIT_DOM_NODE (element
),
15086 webkit_dom_node_append_child (
15087 WEBKIT_DOM_NODE (el
), WEBKIT_DOM_NODE (element
), NULL
);
15091 gboolean no_sibling
;
15092 WebKitDOMNode
*node
, *sibling
;
15094 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
15096 /* Turning the formatting in the middle of element. */
15097 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
15098 no_sibling
= sibling
&&
15099 !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
15100 !webkit_dom_node_get_next_sibling (sibling
);
15103 gboolean do_clone
= TRUE
;
15104 gchar
*text_content
= NULL
;
15105 WebKitDOMNode
*child
;
15107 if ((text_content
= webkit_dom_node_get_text_content (parent
)) &&
15108 (g_strcmp0 (text_content
, UNICODE_ZERO_WIDTH_SPACE
) == 0))
15111 g_free (text_content
);
15114 clone
= webkit_dom_node_clone_node_with_error (
15115 WEBKIT_DOM_NODE (parent
), FALSE
, NULL
);
15117 while ((child
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
))))
15118 webkit_dom_node_insert_before (
15121 webkit_dom_node_get_first_child (clone
),
15124 webkit_dom_node_insert_before (
15125 webkit_dom_node_get_parent_node (parent
),
15127 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent
)),
15132 webkit_dom_node_insert_before (
15133 webkit_dom_node_get_parent_node (parent
),
15134 WEBKIT_DOM_NODE (element
),
15135 webkit_dom_node_get_next_sibling (parent
),
15137 webkit_dom_node_insert_before (
15138 webkit_dom_node_get_parent_node (parent
),
15140 webkit_dom_node_get_next_sibling (parent
),
15143 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) && !no_sibling
) {
15144 webkit_dom_node_insert_before (
15145 webkit_dom_node_get_parent_node (parent
),
15147 webkit_dom_node_get_next_sibling (parent
),
15151 if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent
))) {
15152 WebKitDOMNode
*first_child
;
15154 if ((first_child
= webkit_dom_node_get_first_child (parent
))) {
15155 gchar
*text_content
= NULL
;
15157 text_content
= webkit_dom_node_get_text_content (first_child
);
15159 if (g_strcmp0 (text_content
, UNICODE_ZERO_WIDTH_SPACE
) != 0)
15160 webkit_dom_element_insert_adjacent_text (
15161 WEBKIT_DOM_ELEMENT (parent
),
15163 UNICODE_ZERO_WIDTH_SPACE
,
15166 g_free (text_content
);
15169 remove_node_if_empty (parent
);
15170 remove_node_if_empty (clone
);
15178 selection_set_font_style (EEditorPage
*editor_page
,
15179 EContentEditorCommand command
,
15182 EEditorHistoryEvent
*ev
= NULL
;
15183 EEditorUndoRedoManager
*manager
;
15185 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15187 e_editor_dom_selection_save (editor_page
);
15189 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
15190 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
15191 ev
= g_new0 (EEditorHistoryEvent
, 1);
15192 if (command
== E_CONTENT_EDITOR_COMMAND_BOLD
)
15193 ev
->type
= HISTORY_BOLD
;
15194 else if (command
== E_CONTENT_EDITOR_COMMAND_ITALIC
)
15195 ev
->type
= HISTORY_ITALIC
;
15196 else if (command
== E_CONTENT_EDITOR_COMMAND_UNDERLINE
)
15197 ev
->type
= HISTORY_UNDERLINE
;
15198 else if (command
== E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
)
15199 ev
->type
= HISTORY_STRIKETHROUGH
;
15201 e_editor_dom_selection_get_coordinates (editor_page
,
15202 &ev
->before
.start
.x
,
15203 &ev
->before
.start
.y
,
15205 &ev
->before
.end
.y
);
15207 ev
->data
.style
.from
= !value
;
15208 ev
->data
.style
.to
= value
;
15211 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
15212 const gchar
*element_name
= NULL
;
15214 if (command
== E_CONTENT_EDITOR_COMMAND_BOLD
)
15215 element_name
= "b";
15216 else if (command
== E_CONTENT_EDITOR_COMMAND_ITALIC
)
15217 element_name
= "i";
15218 else if (command
== E_CONTENT_EDITOR_COMMAND_UNDERLINE
)
15219 element_name
= "u";
15220 else if (command
== E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
)
15221 element_name
= "strike";
15224 set_font_style (e_editor_page_get_document (editor_page
), element_name
, value
);
15225 e_editor_dom_selection_restore (editor_page
);
15229 e_editor_dom_selection_restore (editor_page
);
15231 e_editor_dom_exec_command (editor_page
, command
, NULL
);
15234 e_editor_dom_selection_get_coordinates (editor_page
,
15235 &ev
->after
.start
.x
,
15236 &ev
->after
.start
.y
,
15239 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
15242 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
15246 * e_html_editor_selection_set_underline:
15247 * @selection: an #EEditorSelection
15248 * @underline: @TRUE to enable underline, @FALSE to disable
15250 * Toggles underline formatting of current selection or letter at current
15251 * cursor position, depending on whether @underline is @TRUE or @FALSE.
15254 e_editor_dom_selection_set_underline (EEditorPage
*editor_page
,
15255 gboolean underline
)
15257 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15259 if (e_editor_dom_selection_is_underline (editor_page
) == underline
)
15262 selection_set_font_style (
15263 editor_page
, E_CONTENT_EDITOR_COMMAND_UNDERLINE
, underline
);
15267 is_subscript_element (WebKitDOMElement
*element
)
15269 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15272 return element_has_tag (element
, "sub");
15276 * e_html_editor_selection_is_subscript:
15277 * @selection: an #EEditorSelection
15279 * Returns whether current selection or letter at current cursor position
15282 * Returns @TRUE when selection is in subscript, @FALSE otherwise.
15285 e_editor_dom_selection_is_subscript (EEditorPage
*editor_page
)
15287 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15289 return dom_selection_is_font_format (
15290 editor_page
, (IsRightFormatNodeFunc
) is_subscript_element
, NULL
);
15294 * e_html_editor_selection_set_subscript:
15295 * @selection: an #EEditorSelection
15296 * @subscript: @TRUE to enable subscript, @FALSE to disable
15298 * Toggles subscript of current selection or letter at current cursor position,
15299 * depending on whether @subscript is @TRUE or @FALSE.
15302 e_editor_dom_selection_set_subscript (EEditorPage
*editor_page
,
15303 gboolean subscript
)
15305 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15307 if (e_editor_dom_selection_is_subscript (editor_page
) == subscript
)
15310 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_SUBSCRIPT
, NULL
);
15314 is_superscript_element (WebKitDOMElement
*element
)
15316 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15319 return element_has_tag (element
, "sup");
15323 * e_html_editor_selection_is_superscript:
15324 * @selection: an #EEditorSelection
15326 * Returns whether current selection or letter at current cursor position
15327 * is in superscript.
15329 * Returns @TRUE when selection is in superscript, @FALSE otherwise.
15332 e_editor_dom_selection_is_superscript (EEditorPage
*editor_page
)
15334 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15336 return dom_selection_is_font_format (
15337 editor_page
, (IsRightFormatNodeFunc
) is_superscript_element
, NULL
);
15341 * e_html_editor_selection_set_superscript:
15342 * @selection: an #EEditorSelection
15343 * @superscript: @TRUE to enable superscript, @FALSE to disable
15345 * Toggles superscript of current selection or letter at current cursor position,
15346 * depending on whether @superscript is @TRUE or @FALSE.
15349 e_editor_dom_selection_set_superscript (EEditorPage
*editor_page
,
15350 gboolean superscript
)
15352 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15354 if (e_editor_dom_selection_is_superscript (editor_page
) == superscript
)
15357 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT
, NULL
);
15361 is_strikethrough_element (WebKitDOMElement
*element
)
15363 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15366 return element_has_tag (element
, "strike");
15370 * e_html_editor_selection_is_strikethrough:
15371 * @selection: an #EEditorSelection
15373 * Returns whether current selection or letter at current cursor position
15374 * is striked through.
15376 * Returns @TRUE when selection is striked through, @FALSE otherwise.
15379 e_editor_dom_selection_is_strikethrough (EEditorPage
*editor_page
)
15381 gboolean is_strikethrough
;
15383 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15385 is_strikethrough
= e_editor_page_get_strikethrough (editor_page
);
15386 is_strikethrough
= dom_selection_is_font_format (
15387 editor_page
, (IsRightFormatNodeFunc
) is_strikethrough_element
, &is_strikethrough
);
15389 return is_strikethrough
;
15393 * e_html_editor_selection_set_strikethrough:
15394 * @selection: an #EEditorSelection
15395 * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
15397 * Toggles strike through formatting of current selection or letter at current
15398 * cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
15401 e_editor_dom_selection_set_strikethrough (EEditorPage
*editor_page
,
15402 gboolean strikethrough
)
15404 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15406 if (e_editor_dom_selection_is_strikethrough (editor_page
) == strikethrough
)
15409 selection_set_font_style (
15410 editor_page
, E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
, strikethrough
);
15414 is_monospace_element (WebKitDOMElement
*element
)
15417 gboolean ret_val
= FALSE
;
15422 if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element
))
15425 value
= webkit_dom_element_get_attribute (element
, "face");
15426 if (value
&& g_strcmp0 (value
, "monospace") == 0)
15435 * e_html_editor_selection_is_monospaced:
15436 * @selection: an #EEditorSelection
15438 * Returns whether current selection or letter at current cursor position
15441 * Returns @TRUE when selection is monospaced, @FALSE otherwise.
15444 e_editor_dom_selection_is_monospace (EEditorPage
*editor_page
)
15446 gboolean is_monospace
;
15448 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15450 is_monospace
= e_editor_page_get_monospace (editor_page
);
15451 is_monospace
= dom_selection_is_font_format (
15452 editor_page
, (IsRightFormatNodeFunc
) is_monospace_element
, &is_monospace
);
15454 return is_monospace
;
15458 monospace_selection (EEditorPage
*editor_page
,
15459 WebKitDOMElement
*monospace_element
)
15461 WebKitDOMDocument
*document
;
15462 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
15463 WebKitDOMNode
*sibling
, *node
, *monospace
, *block
;
15464 WebKitDOMNodeList
*list
= NULL
;
15465 gboolean selection_end
= FALSE
;
15466 gboolean first
= TRUE
;
15469 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15471 document
= e_editor_page_get_document (editor_page
);
15472 e_editor_dom_selection_save (editor_page
);
15474 selection_start_marker
= webkit_dom_document_get_element_by_id (
15475 document
, "-x-evo-selection-start-marker");
15476 selection_end_marker
= webkit_dom_document_get_element_by_id (
15477 document
, "-x-evo-selection-end-marker");
15479 block
= WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker
)));
15481 monospace
= WEBKIT_DOM_NODE (monospace_element
);
15482 node
= WEBKIT_DOM_NODE (selection_start_marker
);
15483 /* Go through first block in selection. */
15484 while (block
&& node
&& !webkit_dom_node_is_same_node (block
, node
)) {
15485 if (webkit_dom_node_get_next_sibling (node
)) {
15486 /* Prepare the monospaced element. */
15487 monospace
= webkit_dom_node_insert_before (
15488 webkit_dom_node_get_parent_node (node
),
15489 first
? monospace
: webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15490 first
? node
: webkit_dom_node_get_next_sibling (node
),
15495 /* Move the nodes into monospaced element. */
15496 while (((sibling
= webkit_dom_node_get_next_sibling (monospace
)))) {
15497 webkit_dom_node_append_child (monospace
, sibling
, NULL
);
15498 if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker
), sibling
)) {
15499 selection_end
= TRUE
;
15504 node
= webkit_dom_node_get_parent_node (monospace
);
15508 /* Just one block was selected. */
15512 /* Middle blocks (blocks not containing the end of the selection. */
15513 block
= webkit_dom_node_get_next_sibling (block
);
15514 while (block
&& !selection_end
) {
15515 WebKitDOMNode
*next_block
;
15517 selection_end
= webkit_dom_node_contains (
15518 block
, WEBKIT_DOM_NODE (selection_end_marker
));
15523 next_block
= webkit_dom_node_get_next_sibling (block
);
15525 monospace
= webkit_dom_node_insert_before (
15527 webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15528 webkit_dom_node_get_first_child (block
),
15531 while (((sibling
= webkit_dom_node_get_next_sibling (monospace
))))
15532 webkit_dom_node_append_child (monospace
, sibling
, NULL
);
15534 block
= next_block
;
15537 /* Block containing the end of selection. */
15538 node
= WEBKIT_DOM_NODE (selection_end_marker
);
15539 while (block
&& node
&& !webkit_dom_node_is_same_node (block
, node
)) {
15540 monospace
= webkit_dom_node_insert_before (
15541 webkit_dom_node_get_parent_node (node
),
15542 webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15543 webkit_dom_node_get_next_sibling (node
),
15546 while (((sibling
= webkit_dom_node_get_previous_sibling (monospace
)))) {
15547 webkit_dom_node_insert_before (
15550 webkit_dom_node_get_first_child (monospace
),
15554 node
= webkit_dom_node_get_parent_node (monospace
);
15557 /* Merge all the monospace elements inside other monospace elements. */
15558 list
= webkit_dom_document_query_selector_all (
15559 document
, "font[face=monospace] > font[face=monospace]", NULL
);
15560 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
15561 WebKitDOMNode
*item
;
15562 WebKitDOMNode
*child
;
15564 item
= webkit_dom_node_list_item (list
, ii
);
15565 while ((child
= webkit_dom_node_get_first_child (item
))) {
15566 webkit_dom_node_insert_before (
15567 webkit_dom_node_get_parent_node (item
),
15572 remove_node (item
);
15574 g_clear_object (&list
);
15576 /* Merge all the adjacent monospace elements. */
15577 list
= webkit_dom_document_query_selector_all (
15578 document
, "font[face=monospace] + font[face=monospace]", NULL
);
15579 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
15580 WebKitDOMNode
*item
;
15581 WebKitDOMNode
*child
;
15583 item
= webkit_dom_node_list_item (list
, ii
);
15584 /* The + CSS selector will return some false positives as it doesn't
15585 * take text between elements into account so it will return this:
15586 * <font face="monospace">xx</font>yy<font face="monospace">zz</font>
15587 * as valid, but it isn't so we have to check if previous node
15588 * is indeed element or not. */
15589 if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item
))) {
15590 while ((child
= webkit_dom_node_get_first_child (item
))) {
15591 webkit_dom_node_append_child (
15592 webkit_dom_node_get_previous_sibling (item
), child
, NULL
);
15594 remove_node (item
);
15597 g_clear_object (&list
);
15599 e_editor_dom_selection_restore (editor_page
);
15603 unmonospace_selection (EEditorPage
*editor_page
)
15605 WebKitDOMDocument
*document
;
15606 WebKitDOMElement
*selection_start_marker
;
15607 WebKitDOMElement
*selection_end_marker
;
15608 WebKitDOMElement
*selection_start_clone
;
15609 WebKitDOMElement
*selection_end_clone
;
15610 WebKitDOMNode
*sibling
, *node
;
15611 WebKitDOMNode
*block
, *clone
, *monospace
;
15612 gboolean selection_end
= FALSE
;
15614 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15616 document
= e_editor_page_get_document (editor_page
);
15617 e_editor_dom_selection_save (editor_page
);
15619 selection_start_marker
= webkit_dom_document_get_element_by_id (
15620 document
, "-x-evo-selection-start-marker");
15621 selection_end_marker
= webkit_dom_document_get_element_by_id (
15622 document
, "-x-evo-selection-end-marker");
15624 block
= WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker
)));
15626 node
= WEBKIT_DOM_NODE (selection_start_marker
);
15627 monospace
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
15628 while (monospace
&& !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace
)))
15629 monospace
= webkit_dom_node_get_parent_node (monospace
);
15631 /* No monospaced element was found as a parent of selection start node. */
15635 /* Make a clone of current monospaced element. */
15636 clone
= webkit_dom_node_clone_node_with_error (monospace
, TRUE
, NULL
);
15639 /* Remove all the nodes that are after the selection start point as they
15640 * will be in the cloned node. */
15641 while (monospace
&& node
&& !webkit_dom_node_is_same_node (monospace
, node
)) {
15642 WebKitDOMNode
*tmp
;
15643 while (((sibling
= webkit_dom_node_get_next_sibling (node
))))
15644 remove_node (sibling
);
15646 tmp
= webkit_dom_node_get_parent_node (node
);
15647 if (webkit_dom_node_get_next_sibling (node
))
15648 remove_node (node
);
15652 selection_start_clone
= webkit_dom_element_query_selector (
15653 WEBKIT_DOM_ELEMENT (clone
), "#-x-evo-selection-start-marker", NULL
);
15654 selection_end_clone
= webkit_dom_element_query_selector (
15655 WEBKIT_DOM_ELEMENT (clone
), "#-x-evo-selection-end-marker", NULL
);
15657 /* No selection start node in the block where it is supposed to be, return. */
15658 if (!selection_start_clone
)
15661 /* Remove all the nodes until we hit the selection start point as these
15662 * nodes will stay monospaced and they are already in original element. */
15663 node
= webkit_dom_node_get_first_child (clone
);
15665 WebKitDOMNode
*next_sibling
;
15667 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15668 if (webkit_dom_node_get_first_child (node
)) {
15669 if (webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_start_clone
))) {
15670 node
= webkit_dom_node_get_first_child (node
);
15673 remove_node (node
);
15674 } else if (webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_start_clone
)))
15677 remove_node (node
);
15679 node
= next_sibling
;
15682 /* Insert the clone into the tree. Do it after the previous clean up. If
15683 * we would do it the other way the line would contain duplicated text nodes
15684 * and the block would be expading and shrinking while we would modify it. */
15685 webkit_dom_node_insert_before (
15686 webkit_dom_node_get_parent_node (monospace
),
15688 webkit_dom_node_get_next_sibling (monospace
),
15691 /* Move selection start point the right place. */
15692 remove_node (WEBKIT_DOM_NODE (selection_start_marker
));
15693 webkit_dom_node_insert_before (
15694 webkit_dom_node_get_parent_node (clone
),
15695 WEBKIT_DOM_NODE (selection_start_clone
),
15699 /* Move all the nodes the are supposed to lose the monospace formatting
15700 * out of monospaced element. */
15701 node
= webkit_dom_node_get_first_child (clone
);
15703 WebKitDOMNode
*next_sibling
;
15705 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15706 if (webkit_dom_node_get_first_child (node
)) {
15707 if (selection_end_clone
&&
15708 webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_end_clone
))) {
15709 node
= webkit_dom_node_get_first_child (node
);
15712 webkit_dom_node_insert_before (
15713 webkit_dom_node_get_parent_node (clone
),
15717 } else if (selection_end_clone
&&
15718 webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_end_clone
))) {
15719 selection_end
= TRUE
;
15720 webkit_dom_node_insert_before (
15721 webkit_dom_node_get_parent_node (clone
),
15727 webkit_dom_node_insert_before (
15728 webkit_dom_node_get_parent_node (clone
),
15733 node
= next_sibling
;
15736 remove_node_if_empty (clone
);
15737 remove_node_if_empty (monospace
);
15739 /* Just one block was selected and we hit the selection end point. */
15743 /* Middle blocks */
15744 block
= webkit_dom_node_get_next_sibling (block
);
15745 while (block
&& !selection_end
) {
15746 WebKitDOMNode
*next_block
, *child
, *parent
;
15747 WebKitDOMElement
*monospace_element
;
15749 selection_end
= webkit_dom_node_contains (
15750 block
, WEBKIT_DOM_NODE (selection_end_marker
));
15755 next_block
= webkit_dom_node_get_next_sibling (block
);
15757 /* Find the monospaced element and move all the nodes from it and
15758 * finally remove it. */
15759 monospace_element
= webkit_dom_element_query_selector (
15760 WEBKIT_DOM_ELEMENT (block
), "font[face=monospace]", NULL
);
15761 if (!monospace_element
)
15764 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospace_element
));
15765 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospace_element
)))) {
15766 webkit_dom_node_insert_before (
15767 parent
, child
, WEBKIT_DOM_NODE (monospace_element
), NULL
);
15770 remove_node (WEBKIT_DOM_NODE (monospace_element
));
15772 block
= next_block
;
15776 node
= WEBKIT_DOM_NODE (selection_end_marker
);
15777 monospace
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker
));
15778 while (monospace
&& !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace
)))
15779 monospace
= webkit_dom_node_get_parent_node (monospace
);
15781 /* No monospaced element was found as a parent of selection end node. */
15785 clone
= WEBKIT_DOM_NODE (monospace
);
15786 node
= webkit_dom_node_get_first_child (clone
);
15787 /* Move all the nodes that are supposed to lose the monospaced formatting
15788 * out of the monospaced element. */
15790 WebKitDOMNode
*next_sibling
;
15792 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15793 if (webkit_dom_node_get_first_child (node
)) {
15794 if (webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_end_marker
))) {
15795 node
= webkit_dom_node_get_first_child (node
);
15798 webkit_dom_node_insert_before (
15799 webkit_dom_node_get_parent_node (clone
),
15803 } else if (webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_end_marker
))) {
15804 selection_end
= TRUE
;
15805 webkit_dom_node_insert_before (
15806 webkit_dom_node_get_parent_node (clone
),
15812 webkit_dom_node_insert_before (
15813 webkit_dom_node_get_parent_node (clone
),
15819 node
= next_sibling
;
15822 remove_node_if_empty (clone
);
15824 e_editor_dom_selection_restore (editor_page
);
15828 * e_html_editor_selection_set_monospaced:
15829 * @selection: an #EEditorSelection
15830 * @monospaced: @TRUE to enable monospaced, @FALSE to disable
15832 * Toggles monospaced formatting of current selection or letter at current cursor
15833 * position, depending on whether @monospaced is @TRUE or @FALSE.
15836 e_editor_dom_selection_set_monospace (EEditorPage
*editor_page
,
15839 WebKitDOMDocument
*document
;
15840 WebKitDOMRange
*range
= NULL
;
15841 EEditorHistoryEvent
*ev
= NULL
;
15842 EEditorUndoRedoManager
*manager
;
15843 guint font_size
= 0;
15845 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15847 if ((e_editor_dom_selection_is_monospace (editor_page
) ? 1 : 0) == (value
? 1 : 0))
15850 document
= e_editor_page_get_document (editor_page
);
15851 range
= e_editor_dom_get_current_range (editor_page
);
15855 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
15856 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
15857 ev
= g_new0 (EEditorHistoryEvent
, 1);
15858 ev
->type
= HISTORY_MONOSPACE
;
15860 e_editor_dom_selection_get_coordinates (editor_page
,
15861 &ev
->before
.start
.x
,
15862 &ev
->before
.start
.y
,
15864 &ev
->before
.end
.y
);
15866 ev
->data
.style
.from
= !value
;
15867 ev
->data
.style
.to
= value
;
15870 font_size
= e_editor_page_get_font_size (editor_page
);
15871 if (font_size
== 0)
15872 font_size
= E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
15875 WebKitDOMElement
*monospace
;
15877 monospace
= webkit_dom_document_create_element (
15878 document
, "font", NULL
);
15879 webkit_dom_element_set_attribute (
15880 monospace
, "face", "monospace", NULL
);
15881 if (font_size
!= 0) {
15882 gchar
*font_size_str
;
15884 font_size_str
= g_strdup_printf ("%d", font_size
);
15885 webkit_dom_element_set_attribute (
15886 monospace
, "size", font_size_str
, NULL
);
15887 g_free (font_size_str
);
15890 if (!webkit_dom_range_get_collapsed (range
, NULL
))
15891 monospace_selection (editor_page
, monospace
);
15893 /* https://bugs.webkit.org/show_bug.cgi?id=15256 */
15894 webkit_dom_element_set_inner_html (
15896 UNICODE_ZERO_WIDTH_SPACE
,
15898 webkit_dom_range_insert_node (
15899 range
, WEBKIT_DOM_NODE (monospace
), NULL
);
15901 e_editor_dom_move_caret_into_element (editor_page
, monospace
, FALSE
);
15904 gboolean is_bold
= FALSE
, is_italic
= FALSE
;
15905 gboolean is_underline
= FALSE
, is_strikethrough
= FALSE
;
15906 WebKitDOMElement
*tt_element
;
15907 WebKitDOMNode
*node
;
15909 node
= webkit_dom_range_get_end_container (range
, NULL
);
15910 if (WEBKIT_DOM_IS_ELEMENT (node
) &&
15911 is_monospace_element (WEBKIT_DOM_ELEMENT (node
))) {
15912 tt_element
= WEBKIT_DOM_ELEMENT (node
);
15914 tt_element
= dom_node_find_parent_element (node
, "FONT");
15916 if (!is_monospace_element (tt_element
)) {
15917 g_clear_object (&range
);
15923 /* Save current formatting */
15924 is_bold
= e_editor_page_get_bold (editor_page
);
15925 is_italic
= e_editor_page_get_italic (editor_page
);
15926 is_underline
= e_editor_page_get_underline (editor_page
);
15927 is_strikethrough
= e_editor_page_get_strikethrough (editor_page
);
15929 if (!e_editor_dom_selection_is_collapsed (editor_page
))
15930 unmonospace_selection (editor_page
);
15932 e_editor_dom_selection_save (editor_page
);
15933 set_font_style (document
, "", FALSE
);
15934 e_editor_dom_selection_restore (editor_page
);
15937 /* Re-set formatting */
15939 e_editor_dom_selection_set_bold (editor_page
, TRUE
);
15941 e_editor_dom_selection_set_italic (editor_page
, TRUE
);
15943 e_editor_dom_selection_set_underline (editor_page
, TRUE
);
15944 if (is_strikethrough
)
15945 e_editor_dom_selection_set_strikethrough (editor_page
, TRUE
);
15948 e_editor_dom_selection_set_font_size (editor_page
, font_size
);
15952 e_editor_dom_selection_get_coordinates (editor_page
,
15953 &ev
->after
.start
.x
,
15954 &ev
->after
.start
.y
,
15957 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
15960 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
15962 g_clear_object (&range
);
15966 is_bold_element (WebKitDOMElement
*element
)
15968 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15971 if (element_has_tag (element
, "b"))
15974 /* Headings are bold by default */
15975 return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element
);
15979 * e_html_editor_selection_is_bold:
15980 * @selection: an #EEditorSelection
15982 * Returns whether current selection or letter at current cursor position
15985 * Returns @TRUE when selection is bold, @FALSE otherwise.
15988 e_editor_dom_selection_is_bold (EEditorPage
*editor_page
)
15992 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15994 is_bold
= e_editor_page_get_bold (editor_page
);
15995 is_bold
= dom_selection_is_font_format (
15996 editor_page
, (IsRightFormatNodeFunc
) is_bold_element
, &is_bold
);
16002 * e_html_editor_selection_set_bold:
16003 * @selection: an #EEditorSelection
16004 * @bold: @TRUE to enable bold, @FALSE to disable
16006 * Toggles bold formatting of current selection or letter at current cursor
16007 * position, depending on whether @bold is @TRUE or @FALSE.
16010 e_editor_dom_selection_set_bold (EEditorPage
*editor_page
,
16013 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16015 if (e_editor_dom_selection_is_bold (editor_page
) == bold
)
16018 selection_set_font_style (
16019 editor_page
, E_CONTENT_EDITOR_COMMAND_BOLD
, bold
);
16021 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
16025 is_italic_element (WebKitDOMElement
*element
)
16027 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
16030 return element_has_tag (element
, "i") || element_has_tag (element
, "address");
16034 * e_html_editor_selection_is_italic:
16035 * @selection: an #EEditorSelection
16037 * Returns whether current selection or letter at current cursor position
16040 * Returns @TRUE when selection is italic, @FALSE otherwise.
16043 e_editor_dom_selection_is_italic (EEditorPage
*editor_page
)
16045 gboolean is_italic
;
16047 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16049 is_italic
= e_editor_page_get_italic (editor_page
);
16050 is_italic
= dom_selection_is_font_format (
16051 editor_page
, (IsRightFormatNodeFunc
) is_italic_element
, &is_italic
);
16057 * e_html_editor_selection_set_italic:
16058 * @selection: an #EEditorSelection
16059 * @italic: @TRUE to enable italic, @FALSE to disable
16061 * Toggles italic formatting of current selection or letter at current cursor
16062 * position, depending on whether @italic is @TRUE or @FALSE.
16065 e_editor_dom_selection_set_italic (EEditorPage
*editor_page
,
16068 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16070 if (e_editor_dom_selection_is_italic (editor_page
) == italic
)
16073 selection_set_font_style (
16074 editor_page
, E_CONTENT_EDITOR_COMMAND_ITALIC
, italic
);
16078 * e_html_editor_selection_is_indented:
16079 * @selection: an #EEditorSelection
16081 * Returns whether current paragraph is indented. This does not include
16082 * citations. To check, whether paragraph is a citation, use
16083 * e_html_editor_selection_is_citation().
16085 * Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
16088 e_editor_dom_selection_is_indented (EEditorPage
*editor_page
)
16090 WebKitDOMElement
*element
;
16091 WebKitDOMRange
*range
= NULL
;
16093 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16095 range
= e_editor_dom_get_current_range (editor_page
);
16099 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
16100 element
= get_element_for_inspection (range
);
16101 g_clear_object (&range
);
16102 return element_has_class (element
, "-x-evo-indented");
16104 WebKitDOMNode
*node
;
16107 node
= webkit_dom_range_get_end_container (range
, NULL
);
16108 /* No selection or whole body selected */
16109 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
16112 element
= WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
16113 ret_val
= element_has_class (element
, "-x-evo-indented");
16117 node
= webkit_dom_range_get_start_container (range
, NULL
);
16118 /* No selection or whole body selected */
16119 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
16122 element
= WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
16123 ret_val
= element_has_class (element
, "-x-evo-indented");
16125 g_clear_object (&range
);
16131 g_clear_object (&range
);
16137 * e_html_editor_selection_is_citation:
16138 * @selection: an #EEditorSelection
16140 * Returns whether current paragraph is a citation.
16142 * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
16145 e_editor_dom_selection_is_citation (EEditorPage
*editor_page
)
16147 WebKitDOMNode
*node
;
16148 WebKitDOMRange
*range
= NULL
;
16150 gchar
*value
, *text_content
;
16152 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16154 range
= e_editor_dom_get_current_range (editor_page
);
16158 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16159 g_clear_object (&range
);
16161 if (WEBKIT_DOM_IS_TEXT (node
))
16162 return get_has_style (editor_page
, "citation");
16164 text_content
= webkit_dom_node_get_text_content (node
);
16165 if (g_strcmp0 (text_content
, "") == 0) {
16166 g_free (text_content
);
16169 g_free (text_content
);
16171 value
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "type");
16172 /* citation == <blockquote type='cite'> */
16173 if (value
&& strstr (value
, "cite"))
16176 ret_val
= get_has_style (editor_page
, "citation");
16183 get_font_property (EEditorPage
*editor_page
,
16184 const gchar
*font_property
)
16186 WebKitDOMRange
*range
= NULL
;
16187 WebKitDOMNode
*node
;
16188 WebKitDOMElement
*element
;
16191 range
= e_editor_dom_get_current_range (editor_page
);
16195 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16196 g_clear_object (&range
);
16197 element
= dom_node_find_parent_element (node
, "FONT");
16198 while (element
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (element
) &&
16199 !webkit_dom_element_has_attribute (element
, font_property
)) {
16200 element
= dom_node_find_parent_element (
16201 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)), "FONT");
16207 g_object_get (G_OBJECT (element
), font_property
, &value
, NULL
);
16213 * e_editor_dom_selection_get_font_size:
16214 * @selection: an #EEditorSelection
16216 * Returns point size of current selection or of letter at current cursor position.
16219 e_editor_dom_selection_get_font_size (EEditorPage
*editor_page
)
16223 gboolean increment
;
16225 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
16227 size
= get_font_property (editor_page
, "size");
16228 if (!(size
&& *size
)) {
16230 return E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
16233 /* We don't support increments, but when going through a content that
16234 * was not written in Evolution we can find it. In this case just report
16235 * the normal size. */
16236 /* FIXME: go through all parent and get the right value. */
16237 increment
= size
[0] == '+' || size
[0] == '-';
16238 size_int
= atoi (size
);
16241 if (increment
|| size_int
== 0)
16242 return E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
16248 * e_html_editor_selection_set_font_size:
16249 * @selection: an #EEditorSelection
16250 * @font_size: point size to apply
16252 * Sets font size of current selection or of letter at current cursor position
16256 e_editor_dom_selection_set_font_size (EEditorPage
*editor_page
,
16257 EContentEditorFontSize font_size
)
16259 WebKitDOMDocument
*document
;
16260 EEditorUndoRedoManager
*manager
;
16261 EEditorHistoryEvent
*ev
= NULL
;
16263 guint current_font_size
;
16265 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16267 document
= e_editor_page_get_document (editor_page
);
16268 current_font_size
= e_editor_dom_selection_get_font_size (editor_page
);
16269 if (current_font_size
== font_size
)
16272 e_editor_dom_selection_save (editor_page
);
16274 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
16275 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
16276 ev
= g_new0 (EEditorHistoryEvent
, 1);
16277 ev
->type
= HISTORY_FONT_SIZE
;
16279 e_editor_dom_selection_get_coordinates (editor_page
,
16280 &ev
->before
.start
.x
,
16281 &ev
->before
.start
.y
,
16283 &ev
->before
.end
.y
);
16285 ev
->data
.style
.from
= current_font_size
;
16286 ev
->data
.style
.to
= font_size
;
16289 size_str
= g_strdup_printf ("%d", font_size
);
16291 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
16292 WebKitDOMElement
*font
;
16294 font
= set_font_style (document
, "font", font_size
!= 3);
16296 webkit_dom_element_set_attribute (font
, "size", size_str
, NULL
);
16297 e_editor_dom_selection_restore (editor_page
);
16301 e_editor_dom_selection_restore (editor_page
);
16303 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FONT_SIZE
, size_str
);
16305 /* Text in <font size="3"></font> (size 3 is our default size) is a little
16306 * bit smaller than font outsize it. So move it outside of it. */
16307 if (font_size
== E_CONTENT_EDITOR_FONT_SIZE_NORMAL
) {
16308 WebKitDOMElement
*element
;
16310 element
= webkit_dom_document_query_selector (document
, "font[size=\"3\"]", NULL
);
16312 WebKitDOMNode
*child
;
16314 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
))))
16315 webkit_dom_node_insert_before (
16316 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
16318 WEBKIT_DOM_NODE (element
),
16321 remove_node (WEBKIT_DOM_NODE (element
));
16329 e_editor_dom_selection_get_coordinates (editor_page
,
16330 &ev
->after
.start
.x
,
16331 &ev
->after
.start
.y
,
16335 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
16340 * e_html_editor_selection_set_font_name:
16341 * @selection: an #EEditorSelection
16342 * @font_name: a font name to apply
16344 * Sets font name of current selection or of letter at current cursor position
16348 e_editor_dom_selection_set_font_name (EEditorPage
*editor_page
,
16349 const gchar
*font_name
)
16351 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16353 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FONT_NAME
, font_name
);
16357 * e_html_editor_selection_get_font_name:
16358 * @selection: an #EEditorSelection
16360 * Returns name of font used in current selection or at letter at current cursor
16363 * Returns: A string with font name. [transfer-none]
16366 e_editor_dom_selection_get_font_name (EEditorPage
*editor_page
)
16368 WebKitDOMNode
*node
;
16369 WebKitDOMRange
*range
= NULL
;
16370 WebKitDOMCSSStyleDeclaration
*css
= NULL
;
16373 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
16375 range
= e_editor_dom_get_current_range (editor_page
);
16376 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16377 g_clear_object (&range
);
16379 css
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node
));
16380 value
= webkit_dom_css_style_declaration_get_property_value (css
, "fontFamily");
16381 g_clear_object (&css
);
16387 * e_html_editor_selection_set_font_color:
16388 * @selection: an #EEditorSelection
16389 * @rgba: a #GdkRGBA
16391 * Sets font color of current selection or letter at current cursor position to
16392 * color defined in @rgba.
16395 e_editor_dom_selection_set_font_color (EEditorPage
*editor_page
,
16396 const gchar
*color
)
16398 EEditorUndoRedoManager
*manager
;
16399 EEditorHistoryEvent
*ev
= NULL
;
16401 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16403 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
16404 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
16405 ev
= g_new0 (EEditorHistoryEvent
, 1);
16406 ev
->type
= HISTORY_FONT_COLOR
;
16408 e_editor_dom_selection_get_coordinates (editor_page
,
16409 &ev
->before
.start
.x
,
16410 &ev
->before
.start
.y
,
16412 &ev
->before
.end
.y
);
16414 ev
->data
.string
.from
= g_strdup (e_editor_page_get_font_color (editor_page
));
16415 ev
->data
.string
.to
= g_strdup (color
);
16418 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FORE_COLOR
, color
);
16421 ev
->after
.start
.x
= ev
->before
.start
.x
;
16422 ev
->after
.start
.y
= ev
->before
.start
.y
;
16423 ev
->after
.end
.x
= ev
->before
.end
.x
;
16424 ev
->after
.end
.y
= ev
->before
.end
.y
;
16426 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
16431 * e_html_editor_selection_get_font_color:
16432 * @selection: an #EEditorSelection
16433 * @rgba: a #GdkRGBA object to be set to current font color
16435 * Sets @rgba to contain color of current text selection or letter at current
16439 e_editor_dom_selection_get_font_color (EEditorPage
*editor_page
)
16441 WebKitDOMDocument
*document
;
16444 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
16446 document
= e_editor_page_get_document (editor_page
);
16447 color
= get_font_property (editor_page
, "color");
16448 if (!(color
&& *color
)) {
16449 WebKitDOMHTMLElement
*body
;
16451 body
= webkit_dom_document_get_body (document
);
16453 color
= webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body
));
16454 if (!(color
&& *color
)) {
16456 return g_strdup ("#000000");
16464 * e_html_editor_selection_get_block_format:
16465 * @selection: an #EEditorSelection
16467 * Returns block format of current paragraph.
16469 * Returns: #EContentEditorBlockFormat
16471 EContentEditorBlockFormat
16472 e_editor_dom_selection_get_block_format (EEditorPage
*editor_page
)
16474 WebKitDOMNode
*node
;
16475 WebKitDOMRange
*range
= NULL
;
16476 WebKitDOMElement
*element
;
16477 EContentEditorBlockFormat result
;
16479 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
);
16481 range
= e_editor_dom_get_current_range (editor_page
);
16483 return E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
;
16485 node
= webkit_dom_range_get_start_container (range
, NULL
);
16487 if ((element
= dom_node_find_parent_element (node
, "UL"))) {
16488 WebKitDOMElement
*tmp_element
;
16490 tmp_element
= dom_node_find_parent_element (node
, "OL");
16492 if (webkit_dom_node_contains (WEBKIT_DOM_NODE (tmp_element
), WEBKIT_DOM_NODE (element
)))
16493 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16495 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element
));
16497 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
16498 } else if ((element
= dom_node_find_parent_element (node
, "OL")) != NULL
) {
16499 WebKitDOMElement
*tmp_element
;
16501 tmp_element
= dom_node_find_parent_element (node
, "UL");
16503 if (webkit_dom_node_contains (WEBKIT_DOM_NODE (element
), WEBKIT_DOM_NODE (tmp_element
)))
16504 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16506 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element
));
16508 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16509 } else if (dom_node_find_parent_element (node
, "PRE")) {
16510 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
;
16511 } else if (dom_node_find_parent_element (node
, "ADDRESS")) {
16512 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS
;
16513 } else if (dom_node_find_parent_element (node
, "H1")) {
16514 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H1
;
16515 } else if (dom_node_find_parent_element (node
, "H2")) {
16516 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H2
;
16517 } else if (dom_node_find_parent_element (node
, "H3")) {
16518 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H3
;
16519 } else if (dom_node_find_parent_element (node
, "H4")) {
16520 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H4
;
16521 } else if (dom_node_find_parent_element (node
, "H5")) {
16522 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H5
;
16523 } else if (dom_node_find_parent_element (node
, "H6")) {
16524 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H6
;
16526 /* Everything else is a paragraph (normal block) for us */
16527 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
;
16530 g_clear_object (&range
);
16536 change_leading_space_to_nbsp (WebKitDOMNode
*block
)
16538 WebKitDOMNode
*child
;
16540 if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block
))
16543 if ((child
= webkit_dom_node_get_first_child (block
)) &&
16544 WEBKIT_DOM_IS_CHARACTER_DATA (child
)) {
16547 data
= webkit_dom_character_data_substring_data (
16548 WEBKIT_DOM_CHARACTER_DATA (child
), 0, 1, NULL
);
16550 if (data
&& *data
== ' ')
16551 webkit_dom_character_data_replace_data (
16552 WEBKIT_DOM_CHARACTER_DATA (child
), 0, 1, UNICODE_NBSP
, NULL
);
16558 change_trailing_space_in_block_to_nbsp (WebKitDOMNode
*block
)
16560 WebKitDOMNode
*child
;
16562 if ((child
= webkit_dom_node_get_last_child (block
)) &&
16563 WEBKIT_DOM_IS_CHARACTER_DATA (child
)) {
16567 length
= webkit_dom_character_data_get_length (
16568 WEBKIT_DOM_CHARACTER_DATA (child
));
16570 tmp
= webkit_dom_character_data_substring_data (
16571 WEBKIT_DOM_CHARACTER_DATA (child
), length
- 1, 1, NULL
);
16572 if (tmp
&& *tmp
== ' ') {
16573 webkit_dom_character_data_replace_data (
16574 WEBKIT_DOM_CHARACTER_DATA (child
),
16585 change_space_before_selection_to_nbsp (WebKitDOMNode
*node
)
16587 WebKitDOMNode
*prev_sibling
;
16589 if ((prev_sibling
= webkit_dom_node_get_previous_sibling (node
))) {
16590 if (WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling
)) {
16594 length
= webkit_dom_character_data_get_length (
16595 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
16597 tmp
= webkit_dom_character_data_substring_data (
16598 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
), length
- 1, 1, NULL
);
16599 if (tmp
&& *tmp
== ' ') {
16600 webkit_dom_character_data_replace_data (
16601 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
),
16613 process_block_to_block (EEditorPage
*editor_page
,
16614 EContentEditorBlockFormat format
,
16615 const gchar
*value
,
16616 WebKitDOMNode
*block
,
16617 WebKitDOMNode
*end_block
,
16618 WebKitDOMNode
*blockquote
,
16619 gboolean html_mode
)
16621 WebKitDOMDocument
*document
;
16622 WebKitDOMNode
*next_block
;
16623 gboolean after_selection_end
= FALSE
;
16625 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16627 document
= e_editor_page_get_document (editor_page
);
16629 while (!after_selection_end
&& block
) {
16630 gboolean quoted
= FALSE
;
16631 gboolean empty
= FALSE
;
16633 gint citation_level
= 0;
16634 WebKitDOMNode
*child
;
16635 WebKitDOMElement
*element
;
16637 if (e_editor_dom_node_is_citation_node (block
)) {
16640 next_block
= webkit_dom_node_get_next_sibling (block
);
16641 finished
= process_block_to_block (
16645 webkit_dom_node_get_first_child (block
),
16653 block
= next_block
;
16658 if (webkit_dom_element_query_selector (
16659 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
16661 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
16665 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
16667 after_selection_end
= webkit_dom_node_is_same_node (block
, end_block
);
16669 next_block
= webkit_dom_node_get_next_sibling (block
);
16671 if (node_is_list (block
)) {
16672 WebKitDOMNode
*item
;
16674 item
= webkit_dom_node_get_first_child (block
);
16675 while (item
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
))
16676 item
= webkit_dom_node_get_first_child (item
);
16678 if (item
&& do_format_change_list_to_block (editor_page
, format
, item
, value
))
16681 block
= next_block
;
16686 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
)
16687 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
16689 element
= webkit_dom_document_create_element (
16690 document
, value
, NULL
);
16692 content
= webkit_dom_node_get_text_content (block
);
16694 empty
= !*content
|| (g_strcmp0 (content
, UNICODE_ZERO_WIDTH_SPACE
) == 0);
16697 change_leading_space_to_nbsp (block
);
16698 change_trailing_space_in_block_to_nbsp (block
);
16700 while ((child
= webkit_dom_node_get_first_child (block
))) {
16701 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
16704 webkit_dom_node_append_child (
16705 WEBKIT_DOM_NODE (element
), child
, NULL
);
16709 WebKitDOMElement
*br
;
16711 br
= webkit_dom_document_create_element (
16712 document
, "BR", NULL
);
16713 webkit_dom_node_append_child (
16714 WEBKIT_DOM_NODE (element
), WEBKIT_DOM_NODE (br
), NULL
);
16717 webkit_dom_node_insert_before (
16718 webkit_dom_node_get_parent_node (block
),
16719 WEBKIT_DOM_NODE (element
),
16723 remove_node (block
);
16725 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
16727 if (!next_block
&& !after_selection_end
&& citation_level
> 0) {
16728 next_block
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
16729 next_block
= webkit_dom_node_get_next_sibling (next_block
);
16732 block
= next_block
;
16734 if (!html_mode
&& format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
) {
16735 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
16737 if (citation_level
> 0) {
16738 gint quote
, word_wrap_length
;
16741 e_editor_page_get_word_wrap_length (editor_page
);
16742 quote
= citation_level
* 2;
16744 element
= e_editor_dom_wrap_paragraph_length (
16745 editor_page
, element
, word_wrap_length
- quote
);
16750 if (!html_mode
&& quoted
&& citation_level
> 0)
16751 e_editor_dom_quote_plain_text_element_after_wrapping (
16752 editor_page
, element
, citation_level
);
16755 return after_selection_end
;
16759 format_change_block_to_block (EEditorPage
*editor_page
,
16760 EContentEditorBlockFormat format
,
16761 const gchar
*value
)
16763 WebKitDOMDocument
*document
;
16764 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
16765 WebKitDOMNode
*block
, *end_block
, *blockquote
= NULL
;
16766 gboolean html_mode
= FALSE
;
16768 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16770 document
= e_editor_page_get_document (editor_page
);
16771 selection_start_marker
= webkit_dom_document_get_element_by_id (
16772 document
, "-x-evo-selection-start-marker");
16773 selection_end_marker
= webkit_dom_document_get_element_by_id (
16774 document
, "-x-evo-selection-end-marker");
16776 /* If the selection was not saved, move it into the first child of body */
16777 if (!selection_start_marker
|| !selection_end_marker
) {
16778 WebKitDOMHTMLElement
*body
;
16779 WebKitDOMNode
*child
;
16781 body
= webkit_dom_document_get_body (document
);
16782 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
16784 dom_add_selection_markers_into_element_start (
16786 WEBKIT_DOM_ELEMENT (child
),
16787 &selection_start_marker
,
16788 &selection_end_marker
);
16791 block
= e_editor_dom_get_parent_block_node_from_child (
16792 WEBKIT_DOM_NODE (selection_start_marker
));
16794 html_mode
= e_editor_page_get_html_mode (editor_page
);
16796 end_block
= e_editor_dom_get_parent_block_node_from_child (
16797 WEBKIT_DOM_NODE (selection_end_marker
));
16799 /* Process all blocks that are in the selection one by one */
16800 process_block_to_block (
16801 editor_page
, format
, value
, block
, end_block
, blockquote
, html_mode
);
16805 format_change_block_to_list (EEditorPage
*editor_page
,
16806 EContentEditorBlockFormat format
)
16808 WebKitDOMDocument
*document
;
16809 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *item
, *list
;
16810 WebKitDOMNode
*block
, *next_block
;
16811 gboolean after_selection_end
= FALSE
, in_quote
= FALSE
;
16812 gboolean html_mode
= e_editor_page_get_html_mode (editor_page
);
16814 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16816 document
= e_editor_page_get_document (editor_page
);
16817 selection_start_marker
= webkit_dom_document_get_element_by_id (
16818 document
, "-x-evo-selection-start-marker");
16819 selection_end_marker
= webkit_dom_document_get_element_by_id (
16820 document
, "-x-evo-selection-end-marker");
16822 /* If the selection was not saved, move it into the first child of body */
16823 if (!selection_start_marker
|| !selection_end_marker
) {
16824 WebKitDOMHTMLElement
*body
;
16825 WebKitDOMNode
*child
;
16827 body
= webkit_dom_document_get_body (document
);
16828 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
16830 dom_add_selection_markers_into_element_start (
16832 WEBKIT_DOM_ELEMENT (child
),
16833 &selection_start_marker
,
16834 &selection_end_marker
);
16837 block
= e_editor_dom_get_parent_block_node_from_child (
16838 WEBKIT_DOM_NODE (selection_start_marker
));
16840 list
= create_list_element (editor_page
, format
, 0, html_mode
);
16842 if (webkit_dom_element_query_selector (
16843 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
16844 WebKitDOMElement
*element
;
16845 WebKitDOMDOMWindow
*dom_window
= NULL
;
16846 WebKitDOMDOMSelection
*dom_selection
= NULL
;
16847 WebKitDOMRange
*range
= NULL
;
16851 dom_window
= webkit_dom_document_get_default_view (document
);
16852 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
16853 range
= webkit_dom_document_create_range (document
);
16855 webkit_dom_range_select_node (range
, block
, NULL
);
16856 webkit_dom_range_collapse (range
, TRUE
, NULL
);
16857 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
16858 webkit_dom_dom_selection_add_range (dom_selection
, range
);
16860 g_clear_object (&range
);
16861 g_clear_object (&dom_selection
);
16862 g_clear_object (&dom_window
);
16864 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
16865 e_editor_page_block_selection_changed (editor_page
);
16867 e_editor_dom_exec_command (
16868 editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT
, NULL
);
16870 e_editor_dom_register_input_event_listener_on_body (editor_page
);
16871 e_editor_page_unblock_selection_changed (editor_page
);
16873 element
= webkit_dom_document_query_selector (
16874 document
, "body>br", NULL
);
16876 webkit_dom_node_replace_child (
16877 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
16878 WEBKIT_DOM_NODE (list
),
16879 WEBKIT_DOM_NODE (element
),
16882 block
= e_editor_dom_get_parent_block_node_from_child (
16883 WEBKIT_DOM_NODE (selection_start_marker
));
16885 webkit_dom_node_insert_before (
16886 webkit_dom_node_get_parent_node (block
),
16887 WEBKIT_DOM_NODE (list
),
16891 /* Process all blocks that are in the selection one by one */
16892 while (block
&& !after_selection_end
) {
16893 gboolean empty
= FALSE
, block_is_list
;
16895 WebKitDOMNode
*child
, *parent
;
16897 after_selection_end
= webkit_dom_node_contains (
16898 block
, WEBKIT_DOM_NODE (selection_end_marker
));
16900 next_block
= webkit_dom_node_get_next_sibling (
16901 WEBKIT_DOM_NODE (block
));
16903 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
16904 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
16906 item
= webkit_dom_document_create_element (document
, "LI", NULL
);
16907 content
= webkit_dom_node_get_text_content (block
);
16909 empty
= !*content
|| (g_strcmp0 (content
, UNICODE_ZERO_WIDTH_SPACE
) == 0);
16912 change_leading_space_to_nbsp (block
);
16913 change_trailing_space_in_block_to_nbsp (block
);
16915 block_is_list
= node_is_list_or_item (block
);
16917 while ((child
= webkit_dom_node_get_first_child (block
))) {
16918 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
16921 webkit_dom_node_append_child (
16922 WEBKIT_DOM_NODE (block_is_list
? list
: item
), child
, NULL
);
16925 if (!block_is_list
) {
16926 /* We have to use again the hidden space to move caret into newly inserted list */
16928 WebKitDOMElement
*br
;
16930 br
= webkit_dom_document_create_element (
16931 document
, "BR", NULL
);
16932 webkit_dom_node_append_child (
16933 WEBKIT_DOM_NODE (item
), WEBKIT_DOM_NODE (br
), NULL
);
16936 webkit_dom_node_append_child (
16937 WEBKIT_DOM_NODE (list
), WEBKIT_DOM_NODE (item
), NULL
);
16940 parent
= webkit_dom_node_get_parent_node (block
);
16941 remove_node (block
);
16944 /* Remove all parents if previously removed node was the
16945 * only one with text content */
16946 content
= webkit_dom_node_get_text_content (parent
);
16947 while (parent
&& content
&& !*content
) {
16948 WebKitDOMNode
*tmp
= webkit_dom_node_get_parent_node (parent
);
16950 remove_node (parent
);
16954 content
= webkit_dom_node_get_text_content (parent
);
16959 block
= next_block
;
16962 merge_lists_if_possible (WEBKIT_DOM_NODE (list
));
16965 static WebKitDOMElement
*
16966 do_format_change_list_to_list (WebKitDOMElement
*list_to_process
,
16967 WebKitDOMElement
*new_list_template
,
16968 EContentEditorBlockFormat to
)
16970 EContentEditorBlockFormat current_format
;
16972 current_format
= dom_get_list_format_from_node (
16973 WEBKIT_DOM_NODE (list_to_process
));
16974 if (to
== current_format
) {
16975 /* Same format, skip it. */
16976 return list_to_process
;
16977 } else if (current_format
>= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
&&
16978 to
>= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
) {
16979 /* Changing from ordered list type to another ordered list type. */
16980 set_ordered_list_type_to_element (list_to_process
, to
);
16981 return list_to_process
;
16983 WebKitDOMNode
*clone
, *child
;
16985 /* Create new list from template. */
16986 clone
= webkit_dom_node_clone_node_with_error (
16987 WEBKIT_DOM_NODE (new_list_template
), FALSE
, NULL
);
16989 /* Insert it before the list that we are processing. */
16990 webkit_dom_node_insert_before (
16991 webkit_dom_node_get_parent_node (
16992 WEBKIT_DOM_NODE (list_to_process
)),
16994 WEBKIT_DOM_NODE (list_to_process
),
16997 /* Move all it children to the new one. */
16998 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (list_to_process
))))
16999 webkit_dom_node_append_child (clone
, child
, NULL
);
17001 remove_node (WEBKIT_DOM_NODE (list_to_process
));
17003 return WEBKIT_DOM_ELEMENT (clone
);
17010 format_change_list_from_list (EEditorPage
*editor_page
,
17011 EContentEditorBlockFormat to
,
17012 gboolean html_mode
)
17014 WebKitDOMDocument
*document
;
17015 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *new_list
;
17016 WebKitDOMNode
*source_list
, *source_list_clone
, *current_list
, *item
;
17017 gboolean after_selection_end
= FALSE
;
17019 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17021 document
= e_editor_page_get_document (editor_page
);
17022 selection_start_marker
= webkit_dom_document_get_element_by_id (
17023 document
, "-x-evo-selection-start-marker");
17024 selection_end_marker
= webkit_dom_document_get_element_by_id (
17025 document
, "-x-evo-selection-end-marker");
17027 if (!selection_start_marker
|| !selection_end_marker
)
17030 /* Copy elements from previous block to list */
17031 item
= get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start_marker
));
17032 source_list
= webkit_dom_node_get_parent_node (item
);
17033 current_list
= source_list
;
17034 source_list_clone
= webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
);
17036 new_list
= create_list_element (editor_page
, to
, 0, html_mode
);
17038 if (element_has_class (WEBKIT_DOM_ELEMENT (source_list
), "-x-evo-indented"))
17039 element_add_class (WEBKIT_DOM_ELEMENT (new_list
), "-x-evo-indented");
17042 gboolean selection_end
;
17043 WebKitDOMNode
*next_item
= webkit_dom_node_get_next_sibling (item
);
17045 selection_end
= webkit_dom_node_contains (
17046 item
, WEBKIT_DOM_NODE (selection_end_marker
));
17048 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
17049 /* Actual node is an item, just copy it. */
17050 webkit_dom_node_append_child (
17051 after_selection_end
?
17052 source_list_clone
: WEBKIT_DOM_NODE (new_list
),
17055 } else if (node_is_list (item
) && !selection_end
&& !after_selection_end
) {
17056 /* Node is a list and it doesn't contain the selection end
17057 * marker, we can process the whole list. */
17059 WebKitDOMNodeList
*list
= NULL
;
17060 WebKitDOMElement
*processed_list
;
17062 list
= webkit_dom_element_query_selector_all (
17063 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
17064 ii
= webkit_dom_node_list_get_length (list
);
17065 g_clear_object (&list
);
17067 /* Process every sublist separately. */
17069 WebKitDOMElement
*list_to_process
;
17071 list_to_process
= webkit_dom_element_query_selector (
17072 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
17073 if (list_to_process
)
17074 do_format_change_list_to_list (list_to_process
, new_list
, to
);
17078 /* Process the current list. */
17079 processed_list
= do_format_change_list_to_list (
17080 WEBKIT_DOM_ELEMENT (item
), new_list
, to
);
17082 webkit_dom_node_append_child (
17083 WEBKIT_DOM_NODE (new_list
),
17084 WEBKIT_DOM_NODE (processed_list
),
17086 } else if (node_is_list (item
) && !after_selection_end
) {
17087 /* Node is a list and it contains the selection end marker,
17088 * thus we have to process it until we find the marker. */
17090 WebKitDOMNodeList
*list
= NULL
;
17092 list
= webkit_dom_element_query_selector_all (
17093 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
17094 ii
= webkit_dom_node_list_get_length (list
);
17095 g_clear_object (&list
);
17097 /* No nested lists - process the items. */
17099 WebKitDOMNode
*clone
, *child
;
17101 clone
= webkit_dom_node_clone_node_with_error (
17102 WEBKIT_DOM_NODE (new_list
), FALSE
, NULL
);
17104 webkit_dom_node_append_child (
17105 WEBKIT_DOM_NODE (new_list
), clone
, NULL
);
17107 while ((child
= webkit_dom_node_get_first_child (item
))) {
17108 webkit_dom_node_append_child (clone
, child
, NULL
);
17109 if (webkit_dom_node_contains (child
, WEBKIT_DOM_NODE (selection_end_marker
)))
17113 if (webkit_dom_node_get_first_child (item
))
17114 webkit_dom_node_append_child (
17115 WEBKIT_DOM_NODE (new_list
), item
, NULL
);
17117 remove_node (item
);
17119 gboolean done
= FALSE
;
17120 WebKitDOMNode
*tmp_parent
= WEBKIT_DOM_NODE (new_list
);
17121 WebKitDOMNode
*tmp_item
= WEBKIT_DOM_NODE (item
);
17124 WebKitDOMNode
*clone
, *child
;
17126 clone
= webkit_dom_node_clone_node_with_error (
17127 WEBKIT_DOM_NODE (new_list
), FALSE
, NULL
);
17129 webkit_dom_node_append_child (
17130 tmp_parent
, clone
, NULL
);
17132 while ((child
= webkit_dom_node_get_first_child (tmp_item
))) {
17133 if (!webkit_dom_node_contains (child
, WEBKIT_DOM_NODE (selection_end_marker
))) {
17134 webkit_dom_node_append_child (clone
, child
, NULL
);
17135 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (child
)) {
17136 webkit_dom_node_append_child (clone
, child
, NULL
);
17140 tmp_parent
= clone
;
17148 webkit_dom_node_append_child (
17149 after_selection_end
?
17150 source_list_clone
: WEBKIT_DOM_NODE (new_list
),
17155 if (selection_end
) {
17156 source_list_clone
= webkit_dom_node_clone_node_with_error (current_list
, FALSE
, NULL
);
17157 after_selection_end
= TRUE
;
17161 if (after_selection_end
)
17164 current_list
= webkit_dom_node_get_next_sibling (current_list
);
17165 if (!node_is_list_or_item (current_list
))
17167 if (node_is_list (current_list
)) {
17168 next_item
= webkit_dom_node_get_first_child (current_list
);
17169 if (!node_is_list_or_item (next_item
))
17171 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (current_list
)) {
17172 next_item
= current_list
;
17173 current_list
= webkit_dom_node_get_parent_node (next_item
);
17180 webkit_dom_node_insert_before (
17181 webkit_dom_node_get_parent_node (source_list
),
17182 WEBKIT_DOM_NODE (source_list_clone
),
17183 webkit_dom_node_get_next_sibling (source_list
),
17186 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list
)))
17187 webkit_dom_node_insert_before (
17188 webkit_dom_node_get_parent_node (source_list_clone
),
17189 WEBKIT_DOM_NODE (new_list
),
17193 remove_node_if_empty (source_list
);
17194 remove_node_if_empty (source_list_clone
);
17195 remove_node_if_empty (current_list
);
17197 merge_lists_if_possible (WEBKIT_DOM_NODE (new_list
));
17201 format_change_list_to_list (EEditorPage
*editor_page
,
17202 EContentEditorBlockFormat format
,
17203 gboolean html_mode
)
17205 WebKitDOMDocument
*document
;
17206 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
17207 WebKitDOMNode
*prev_list
, *current_list
, *next_list
;
17208 EContentEditorBlockFormat prev
= 0, next
= 0;
17209 gboolean done
= FALSE
, indented
= FALSE
;
17210 gboolean selection_starts_in_first_child
, selection_ends_in_last_child
;
17212 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17214 document
= e_editor_page_get_document (editor_page
);
17215 selection_start_marker
= webkit_dom_document_get_element_by_id (
17216 document
, "-x-evo-selection-start-marker");
17217 selection_end_marker
= webkit_dom_document_get_element_by_id (
17218 document
, "-x-evo-selection-end-marker");
17220 current_list
= get_list_node_from_child (
17221 WEBKIT_DOM_NODE (selection_start_marker
));
17223 prev_list
= get_list_node_from_child (
17224 WEBKIT_DOM_NODE (selection_start_marker
));
17226 next_list
= get_list_node_from_child (
17227 WEBKIT_DOM_NODE (selection_end_marker
));
17229 selection_starts_in_first_child
=
17230 webkit_dom_node_contains (
17231 webkit_dom_node_get_first_child (current_list
),
17232 WEBKIT_DOM_NODE (selection_start_marker
));
17234 selection_ends_in_last_child
=
17235 webkit_dom_node_contains (
17236 webkit_dom_node_get_last_child (current_list
),
17237 WEBKIT_DOM_NODE (selection_end_marker
));
17239 indented
= element_has_class (WEBKIT_DOM_ELEMENT (current_list
), "-x-evo-indented");
17241 if (!prev_list
|| !next_list
|| indented
) {
17242 format_change_list_from_list (editor_page
, format
, html_mode
);
17246 if (webkit_dom_node_is_same_node (prev_list
, next_list
)) {
17247 prev_list
= webkit_dom_node_get_previous_sibling (
17248 webkit_dom_node_get_parent_node (
17249 webkit_dom_node_get_parent_node (
17250 WEBKIT_DOM_NODE (selection_start_marker
))));
17251 next_list
= webkit_dom_node_get_next_sibling (
17252 webkit_dom_node_get_parent_node (
17253 webkit_dom_node_get_parent_node (
17254 WEBKIT_DOM_NODE (selection_end_marker
))));
17255 if (!prev_list
|| !next_list
) {
17256 format_change_list_from_list (editor_page
, format
, html_mode
);
17261 prev
= dom_get_list_format_from_node (prev_list
);
17262 next
= dom_get_list_format_from_node (next_list
);
17264 if (format
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17265 if (format
== prev
&& prev
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17266 if (selection_starts_in_first_child
&& selection_ends_in_last_child
) {
17268 merge_list_into_list (current_list
, prev_list
, FALSE
);
17271 if (format
== next
&& next
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17272 if (selection_starts_in_first_child
&& selection_ends_in_last_child
) {
17274 merge_list_into_list (next_list
, prev_list
, FALSE
);
17282 format_change_list_from_list (editor_page
, format
, html_mode
);
17286 * e_html_editor_selection_set_block_format:
17287 * @selection: an #EEditorSelection
17288 * @format: an #EContentEditorBlockFormat value
17290 * Changes block format of current paragraph to @format.
17293 e_editor_dom_selection_set_block_format (EEditorPage
*editor_page
,
17294 EContentEditorBlockFormat format
)
17296 WebKitDOMDocument
*document
;
17297 WebKitDOMRange
*range
= NULL
;
17298 EContentEditorBlockFormat current_format
;
17299 EContentEditorAlignment current_alignment
;
17300 EEditorUndoRedoManager
*manager
;
17301 EEditorHistoryEvent
*ev
= NULL
;
17302 const gchar
*value
;
17303 gboolean from_list
= FALSE
, to_list
= FALSE
, html_mode
= FALSE
;
17305 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17307 document
= e_editor_page_get_document (editor_page
);
17308 current_format
= e_editor_dom_selection_get_block_format (editor_page
);
17309 if (current_format
== format
)
17313 case E_CONTENT_EDITOR_BLOCK_FORMAT_H1
:
17316 case E_CONTENT_EDITOR_BLOCK_FORMAT_H2
:
17319 case E_CONTENT_EDITOR_BLOCK_FORMAT_H3
:
17322 case E_CONTENT_EDITOR_BLOCK_FORMAT_H4
:
17325 case E_CONTENT_EDITOR_BLOCK_FORMAT_H5
:
17328 case E_CONTENT_EDITOR_BLOCK_FORMAT_H6
:
17331 case E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
:
17334 case E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
:
17337 case E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS
:
17340 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
:
17341 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
:
17342 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN
:
17346 case E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
:
17350 case E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
:
17356 html_mode
= e_editor_page_get_html_mode (editor_page
);
17359 current_format
>= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
17361 range
= e_editor_dom_get_current_range (editor_page
);
17365 current_alignment
= e_editor_page_get_alignment (editor_page
);
17367 e_editor_dom_selection_save (editor_page
);
17369 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17370 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17371 ev
= g_new0 (EEditorHistoryEvent
, 1);
17372 ev
->type
= HISTORY_BLOCK_FORMAT
;
17374 e_editor_dom_selection_get_coordinates (editor_page
,
17375 &ev
->before
.start
.x
,
17376 &ev
->before
.start
.y
,
17378 &ev
->before
.end
.y
);
17380 ev
->data
.style
.from
= current_format
;
17381 ev
->data
.style
.to
= format
;
17384 g_clear_object (&range
);
17386 if (current_format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
) {
17387 WebKitDOMElement
*selection_marker
;
17389 selection_marker
= webkit_dom_document_get_element_by_id (
17390 document
, "-x-evo-selection-start-marker");
17391 if (selection_marker
)
17392 change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker
));
17393 selection_marker
= webkit_dom_document_get_element_by_id (
17394 document
, "-x-evo-selection-end-marker");
17395 if (selection_marker
)
17396 change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker
));
17399 if (from_list
&& to_list
)
17400 format_change_list_to_list (editor_page
, format
, html_mode
);
17402 if (!from_list
&& !to_list
)
17403 format_change_block_to_block (editor_page
, format
, value
);
17405 if (from_list
&& !to_list
)
17406 format_change_list_to_block (editor_page
, format
, value
);
17408 if (!from_list
&& to_list
)
17409 format_change_block_to_list (editor_page
, format
);
17411 e_editor_dom_selection_restore (editor_page
);
17413 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17415 /* When changing the format we need to re-set the alignment */
17416 e_editor_dom_selection_set_alignment (editor_page
, current_alignment
);
17418 e_editor_page_emit_content_changed (editor_page
);
17421 e_editor_dom_selection_get_coordinates (editor_page
,
17422 &ev
->after
.start
.x
,
17423 &ev
->after
.start
.y
,
17426 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17431 * e_html_editor_selection_get_background_color:
17432 * @selection: an #EEditorSelection
17434 * Returns background color of currently selected text or letter at current
17437 * Returns: A string with code of current background color.
17440 e_editor_dom_selection_get_background_color (EEditorPage
*editor_page
)
17442 WebKitDOMNode
*ancestor
;
17443 WebKitDOMRange
*range
= NULL
;
17444 WebKitDOMCSSStyleDeclaration
*css
= NULL
;
17447 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17449 range
= e_editor_dom_get_current_range (editor_page
);
17450 ancestor
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
17451 css
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor
));
17453 g_free (selection->priv->background_color);
17454 selection->priv->background_color =
17455 webkit_dom_css_style_declaration_get_property_value (
17456 css, "background-color");*/
17458 value
= webkit_dom_css_style_declaration_get_property_value (css
, "background-color");
17460 g_clear_object (&css
);
17461 g_clear_object (&range
);
17467 * e_html_editor_selection_set_background_color:
17468 * @selection: an #EEditorSelection
17469 * @color: code of new background color to set
17471 * Changes background color of current selection or letter at current cursor
17472 * position to @color.
17475 e_editor_dom_selection_set_background_color (EEditorPage
*editor_page
,
17476 const gchar
*color
)
17478 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17480 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR
, color
);
17484 * e_html_editor_selection_get_alignment:
17485 * @selection: #an EEditorSelection
17487 * Returns alignment of current paragraph
17489 * Returns: #EContentEditorAlignment
17491 EContentEditorAlignment
17492 e_editor_dom_selection_get_alignment (EEditorPage
*editor_page
)
17494 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
17495 WebKitDOMElement
*element
;
17496 WebKitDOMNode
*node
;
17497 WebKitDOMRange
*range
= NULL
;
17498 EContentEditorAlignment alignment
;
17501 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_ALIGNMENT_LEFT
);
17503 range
= e_editor_dom_get_current_range (editor_page
);
17505 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17509 node
= webkit_dom_range_get_start_container (range
, NULL
);
17510 g_clear_object (&range
);
17512 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17516 if (WEBKIT_DOM_IS_ELEMENT (node
))
17517 element
= WEBKIT_DOM_ELEMENT (node
);
17519 element
= webkit_dom_node_get_parent_element (node
);
17521 if (element_has_class (element
, "-x-evo-align-right")) {
17522 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17524 } else if (element_has_class (element
, "-x-evo-align-center")) {
17525 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17529 style
= webkit_dom_element_get_style (element
);
17530 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
17532 if (!value
|| !*value
||
17533 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
17534 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17535 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
17536 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17537 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
17538 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17540 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17543 g_clear_object (&style
);
17551 set_block_alignment (WebKitDOMElement
*element
,
17552 const gchar
*class)
17554 WebKitDOMElement
*parent
;
17556 element_remove_class (element
, "-x-evo-align-center");
17557 element_remove_class (element
, "-x-evo-align-right");
17558 element_add_class (element
, class);
17559 parent
= webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element
));
17560 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17561 element_remove_class (parent
, "-x-evo-align-center");
17562 element_remove_class (parent
, "-x-evo-align-right");
17563 parent
= webkit_dom_node_get_parent_element (
17564 WEBKIT_DOM_NODE (parent
));
17569 * e_html_editor_selection_set_alignment:
17570 * @selection: an #EEditorSelection
17571 * @alignment: an #EContentEditorAlignment value to apply
17573 * Sets alignment of current paragraph to give @alignment.
17576 e_editor_dom_selection_set_alignment (EEditorPage
*editor_page
,
17577 EContentEditorAlignment alignment
)
17579 WebKitDOMDocument
*document
;
17580 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
17581 WebKitDOMNode
*block
;
17582 EContentEditorAlignment current_alignment
;
17583 EEditorUndoRedoManager
*manager
;
17584 EEditorHistoryEvent
*ev
= NULL
;
17585 gboolean after_selection_end
= FALSE
;
17586 const gchar
*class = "";
17588 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17590 document
= e_editor_page_get_document (editor_page
);
17591 current_alignment
= e_editor_page_get_alignment (editor_page
);
17593 if (current_alignment
== alignment
)
17596 switch (alignment
) {
17597 case E_CONTENT_EDITOR_ALIGNMENT_CENTER
:
17598 class = "-x-evo-align-center";
17601 case E_CONTENT_EDITOR_ALIGNMENT_LEFT
:
17604 case E_CONTENT_EDITOR_ALIGNMENT_RIGHT
:
17605 class = "-x-evo-align-right";
17609 e_editor_dom_selection_save (editor_page
);
17611 selection_start_marker
= webkit_dom_document_get_element_by_id (
17612 document
, "-x-evo-selection-start-marker");
17613 selection_end_marker
= webkit_dom_document_get_element_by_id (
17614 document
, "-x-evo-selection-end-marker");
17616 if (!selection_start_marker
)
17619 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17620 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17621 ev
= g_new0 (EEditorHistoryEvent
, 1);
17622 ev
->type
= HISTORY_ALIGNMENT
;
17624 e_editor_dom_selection_get_coordinates (editor_page
,
17625 &ev
->before
.start
.x
,
17626 &ev
->before
.start
.y
,
17628 &ev
->before
.end
.y
);
17629 ev
->data
.style
.from
= current_alignment
;
17630 ev
->data
.style
.to
= alignment
;
17633 block
= e_editor_dom_get_parent_block_node_from_child (
17634 WEBKIT_DOM_NODE (selection_start_marker
));
17636 while (block
&& !after_selection_end
) {
17637 WebKitDOMNode
*next_block
;
17639 next_block
= webkit_dom_node_get_next_sibling (block
);
17641 after_selection_end
= webkit_dom_node_contains (
17642 block
, WEBKIT_DOM_NODE (selection_end_marker
));
17644 if (element_has_class (WEBKIT_DOM_ELEMENT (block
), "-x-evo-indented")) {
17646 WebKitDOMNodeList
*list
= NULL
;
17648 list
= webkit_dom_element_query_selector_all (
17649 WEBKIT_DOM_ELEMENT (block
),
17650 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
17652 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
17653 WebKitDOMNode
*item
= webkit_dom_node_list_item (list
, ii
);
17655 set_block_alignment (WEBKIT_DOM_ELEMENT (item
), class);
17657 after_selection_end
= webkit_dom_node_contains (
17658 item
, WEBKIT_DOM_NODE (selection_end_marker
));
17659 if (after_selection_end
)
17663 g_clear_object (&list
);
17665 set_block_alignment (WEBKIT_DOM_ELEMENT (block
), class);
17668 block
= next_block
;
17672 e_editor_dom_selection_get_coordinates (editor_page
,
17673 &ev
->after
.start
.x
,
17674 &ev
->after
.start
.y
,
17677 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17680 e_editor_dom_selection_restore (editor_page
);
17682 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17683 e_editor_page_emit_content_changed (editor_page
);
17687 e_editor_dom_insert_replace_all_history_event (EEditorPage
*editor_page
,
17688 const gchar
*search_text
,
17689 const gchar
*replacement
)
17691 EEditorUndoRedoManager
*manager
;
17693 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17695 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17697 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17698 EEditorHistoryEvent
*ev
= g_new0 (EEditorHistoryEvent
, 1);
17699 ev
->type
= HISTORY_REPLACE_ALL
;
17701 ev
->data
.string
.from
= g_strdup (search_text
);
17702 ev
->data
.string
.to
= g_strdup (replacement
);
17704 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17709 * e_html_editor_selection_replace:
17710 * @selection: an #EEditorSelection
17711 * @replacement: a string to replace current selection with
17713 * Replaces currently selected text with @replacement.
17716 e_editor_dom_selection_replace (EEditorPage
*editor_page
,
17717 const gchar
*replacement
)
17719 EEditorHistoryEvent
*ev
= NULL
;
17720 EEditorUndoRedoManager
*manager
;
17721 WebKitDOMRange
*range
= NULL
;
17723 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17725 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17727 if (!(range
= e_editor_dom_get_current_range (editor_page
)) ||
17728 e_editor_dom_selection_is_collapsed (editor_page
))
17731 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17732 ev
= g_new0 (EEditorHistoryEvent
, 1);
17733 ev
->type
= HISTORY_REPLACE
;
17735 e_editor_dom_selection_get_coordinates (editor_page
,
17736 &ev
->before
.start
.x
,
17737 &ev
->before
.start
.y
,
17739 &ev
->before
.end
.y
);
17741 ev
->data
.string
.from
= webkit_dom_range_get_text (range
);
17742 ev
->data
.string
.to
= g_strdup (replacement
);
17745 g_clear_object (&range
);
17747 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT
, replacement
);
17750 e_editor_dom_selection_get_coordinates (editor_page
,
17751 &ev
->after
.start
.x
,
17752 &ev
->after
.start
.y
,
17756 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17759 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17761 e_editor_page_emit_content_changed (editor_page
);
17765 * e_html_editor_selection_replace_caret_word:
17766 * @selection: an #EEditorSelection
17767 * @replacement: a string to replace current caret word with
17769 * Replaces current word under cursor with @replacement.
17772 e_editor_dom_replace_caret_word (EEditorPage
*editor_page
,
17773 const gchar
*replacement
)
17775 WebKitDOMDocument
*document
;
17776 WebKitDOMDOMWindow
*dom_window
= NULL
;
17777 WebKitDOMDOMSelection
*dom_selection
= NULL
;
17778 WebKitDOMDocumentFragment
*fragment
;
17779 WebKitDOMNode
*node
;
17780 WebKitDOMRange
*range
= NULL
;
17782 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17784 document
= e_editor_page_get_document (editor_page
);
17785 dom_window
= webkit_dom_document_get_default_view (document
);
17786 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
17787 g_clear_object (&dom_window
);
17789 e_editor_page_emit_content_changed (editor_page
);
17790 range
= e_editor_dom_get_current_range (editor_page
);
17791 webkit_dom_range_expand (range
, "word", NULL
);
17792 webkit_dom_dom_selection_add_range (dom_selection
, range
);
17794 fragment
= webkit_dom_range_extract_contents (range
, NULL
);
17796 /* Get the text node to replace and leave other formatting nodes
17797 * untouched (font color, boldness, ...). */
17798 webkit_dom_node_normalize (WEBKIT_DOM_NODE (fragment
));
17799 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
17800 if (!WEBKIT_DOM_IS_TEXT (node
)) {
17801 while (node
&& WEBKIT_DOM_IS_ELEMENT (node
))
17802 node
= webkit_dom_node_get_first_child (node
);
17805 if (node
&& WEBKIT_DOM_IS_TEXT (node
)) {
17806 WebKitDOMText
*text
;
17808 /* Replace the word */
17809 text
= webkit_dom_document_create_text_node (document
, replacement
);
17810 webkit_dom_node_replace_child (
17811 webkit_dom_node_get_parent_node (node
),
17812 WEBKIT_DOM_NODE (text
),
17816 /* Insert the word on current location. */
17817 webkit_dom_range_insert_node (range
, WEBKIT_DOM_NODE (fragment
), NULL
);
17819 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
17822 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17824 g_clear_object (&range
);
17825 g_clear_object (&dom_selection
);
17829 * e_html_editor_selection_get_caret_word:
17830 * @selection: an #EEditorSelection
17832 * Returns word under cursor.
17834 * Returns: A newly allocated string with current caret word or @NULL when there
17835 * is no text under cursor or when selection is active. [transfer-full].
17838 e_editor_dom_get_caret_word (EEditorPage
*editor_page
)
17841 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
17843 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17845 range
= e_editor_dom_get_current_range (editor_page
);
17847 /* Don't operate on the visible selection */
17848 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
17849 webkit_dom_range_expand (range_clone
, "word", NULL
);
17850 word
= webkit_dom_range_to_string (range_clone
, NULL
);
17852 g_clear_object (&range
);
17853 g_clear_object (&range_clone
);
17859 * e_html_editor_selection_get_list_alignment_from_node:
17860 * @node: #an WebKitDOMNode
17862 * Returns alignment of given list.
17864 * Returns: #EContentEditorAlignment
17866 EContentEditorAlignment
17867 e_editor_dom_get_list_alignment_from_node (WebKitDOMNode
*node
)
17869 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-center"))
17870 return E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17871 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-right"))
17872 return E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17874 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17878 e_editor_dom_prepare_paragraph (EEditorPage
*editor_page
,
17879 gboolean with_selection
)
17881 WebKitDOMDocument
*document
;
17882 WebKitDOMElement
*element
, *paragraph
;
17884 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17886 document
= e_editor_page_get_document (editor_page
);
17887 paragraph
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
17889 if (with_selection
)
17890 dom_add_selection_markers_into_element_start (
17891 document
, paragraph
, NULL
, NULL
);
17893 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
17895 webkit_dom_node_append_child (
17896 WEBKIT_DOM_NODE (paragraph
), WEBKIT_DOM_NODE (element
), NULL
);
17902 e_editor_dom_selection_set_on_point (EEditorPage
*editor_page
,
17906 WebKitDOMDocument
*document
;
17907 WebKitDOMRange
*range
= NULL
;
17908 WebKitDOMDOMWindow
*dom_window
= NULL
;
17909 WebKitDOMDOMSelection
*dom_selection
= NULL
;
17911 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17913 document
= e_editor_page_get_document (editor_page
);
17914 dom_window
= webkit_dom_document_get_default_view (document
);
17915 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
17917 range
= webkit_dom_document_caret_range_from_point (document
, x
, y
);
17918 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
17919 webkit_dom_dom_selection_add_range (dom_selection
, range
);
17921 g_clear_object (&range
);
17922 g_clear_object (&dom_selection
);
17923 g_clear_object (&dom_window
);
17927 e_editor_dom_selection_get_coordinates (EEditorPage
*editor_page
,
17933 WebKitDOMDocument
*document
;
17934 WebKitDOMElement
*element
, *parent
;
17935 gboolean created_selection_markers
= FALSE
;
17936 guint local_x
= 0, local_y
= 0;
17938 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17939 g_return_if_fail (start_x
!= NULL
);
17940 g_return_if_fail (start_y
!= NULL
);
17941 g_return_if_fail (end_x
!= NULL
);
17942 g_return_if_fail (end_y
!= NULL
);
17944 document
= e_editor_page_get_document (editor_page
);
17945 element
= webkit_dom_document_get_element_by_id (
17946 document
, "-x-evo-selection-start-marker");
17948 created_selection_markers
= TRUE
;
17949 e_editor_dom_selection_save (editor_page
);
17950 element
= webkit_dom_document_get_element_by_id (
17951 document
, "-x-evo-selection-start-marker");
17957 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17958 local_x
+= (guint
) webkit_dom_element_get_offset_left (parent
);
17959 local_y
+= (guint
) webkit_dom_element_get_offset_top (parent
);
17960 parent
= webkit_dom_element_get_offset_parent (parent
);
17963 *start_x
= local_x
;
17964 *start_y
= local_y
;
17966 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
17970 if (created_selection_markers
)
17971 e_editor_dom_selection_restore (editor_page
);
17976 element
= webkit_dom_document_get_element_by_id (
17977 document
, "-x-evo-selection-end-marker");
17983 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17984 local_x
+= (guint
) webkit_dom_element_get_offset_left (parent
);
17985 local_y
+= (guint
) webkit_dom_element_get_offset_top (parent
);
17986 parent
= webkit_dom_element_get_offset_parent (parent
);
17992 if (created_selection_markers
)
17993 e_editor_dom_selection_restore (editor_page
);
17996 /* Workaround for bug 749712 on the Evolution side. The cause of the bug
17997 * is that WebKit is having problems determining the right line height
17998 * for some fonts and font sizes (the right and wrong value differ by 1).
17999 * To fix this we will add an extra one to the final top offset. This is
18000 * safe to do even for fonts and font sizes that don't behave badly as we
18001 * will still get the right element as we use fonts bigger than 1 pixel. */
18007 e_editor_dom_get_range_for_point (WebKitDOMDocument
*document
,
18008 EEditorSelectionPoint point
)
18010 glong scroll_left
, scroll_top
;
18011 WebKitDOMHTMLElement
*body
;
18012 WebKitDOMRange
*range
= NULL
;
18014 body
= webkit_dom_document_get_body (document
);
18015 scroll_left
= webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body
));
18016 scroll_top
= webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body
));
18018 range
= webkit_dom_document_caret_range_from_point (
18019 document
, point
.x
- scroll_left
, point
.y
- scroll_top
);
18021 /* The point is outside the viewport, scroll to it. */
18023 WebKitDOMDOMWindow
*dom_window
= NULL
;
18025 dom_window
= webkit_dom_document_get_default_view (document
);
18026 webkit_dom_dom_window_scroll_to (dom_window
, point
.x
, point
.y
);
18028 scroll_left
= webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body
));
18029 scroll_top
= webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body
));
18030 range
= webkit_dom_document_caret_range_from_point (
18031 document
, point
.x
- scroll_left
, point
.y
- scroll_top
);
18032 g_clear_object (&dom_window
);
18039 e_editor_dom_selection_restore_to_history_event_state (EEditorPage
*editor_page
,
18040 EEditorSelection selection_state
)
18042 WebKitDOMDocument
*document
;
18043 WebKitDOMDOMWindow
*dom_window
= NULL
;
18044 WebKitDOMDOMSelection
*dom_selection
= NULL
;
18045 WebKitDOMElement
*element
, *tmp
;
18046 WebKitDOMRange
*range
= NULL
;
18047 gboolean was_collapsed
= FALSE
;
18049 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
18051 document
= e_editor_page_get_document (editor_page
);
18052 dom_window
= webkit_dom_document_get_default_view (document
);
18053 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
18054 g_clear_object (&dom_window
);
18056 /* Restore the selection how it was before the event occured. */
18057 range
= e_editor_dom_get_range_for_point (document
, selection_state
.start
);
18058 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
18059 webkit_dom_dom_selection_add_range (dom_selection
, range
);
18060 g_clear_object (&range
);
18062 was_collapsed
= selection_state
.start
.x
== selection_state
.end
.x
;
18063 was_collapsed
= was_collapsed
&& selection_state
.start
.y
== selection_state
.end
.y
;
18064 if (was_collapsed
) {
18065 g_clear_object (&dom_selection
);
18069 e_editor_dom_selection_save (editor_page
);
18071 element
= webkit_dom_document_get_element_by_id (
18072 document
, "-x-evo-selection-end-marker");
18074 remove_node (WEBKIT_DOM_NODE (element
));
18076 element
= webkit_dom_document_get_element_by_id (
18077 document
, "-x-evo-selection-start-marker");
18079 webkit_dom_element_remove_attribute (element
, "id");
18081 range
= e_editor_dom_get_range_for_point (document
, selection_state
.end
);
18082 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
18083 webkit_dom_dom_selection_add_range (dom_selection
, range
);
18084 g_clear_object (&range
);
18086 e_editor_dom_selection_save (editor_page
);
18088 tmp
= webkit_dom_document_get_element_by_id (
18089 document
, "-x-evo-selection-start-marker");
18091 remove_node (WEBKIT_DOM_NODE (tmp
));
18093 webkit_dom_element_set_id (
18094 element
, "-x-evo-selection-start-marker");
18096 e_editor_dom_selection_restore (editor_page
);
18098 g_clear_object (&dom_selection
);