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 adapt_to_editor_dom_changes (WebKitDOMDocument
*document
)
8742 WebKitDOMHTMLCollection
*collection
= NULL
;
8745 /* Normal block code div.-x-evo-paragraph replaced by div[data-evo-paragraph] */
8746 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (document
, "-x-evo-paragraph");
8747 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
8748 WebKitDOMNode
*node
;
8750 node
= webkit_dom_html_collection_item (collection
, ii
);
8751 element_remove_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-paragraph");
8752 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-evo-paragraph", "", NULL
);
8754 g_clear_object (&collection
);
8758 e_editor_dom_process_content_after_load (EEditorPage
*editor_page
)
8761 gint16 start_at_bottom
= -1, top_signature
= -1;
8762 WebKitDOMDocument
*document
;
8763 WebKitDOMHTMLElement
*body
;
8764 WebKitDOMDOMWindow
*dom_window
= NULL
;
8766 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
8768 document
= e_editor_page_get_document (editor_page
);
8770 /* Don't use CSS when possible to preserve compatibility with older
8771 * versions of Evolution or other MUAs */
8772 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS
, "false");
8773 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR
, "div");
8775 body
= webkit_dom_document_get_body (document
);
8777 webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body
), "style");
8778 html_mode
= e_editor_page_get_html_mode (editor_page
);
8780 webkit_dom_element_set_attribute (
8781 WEBKIT_DOM_ELEMENT (body
), "data-evo-plain-text", "", NULL
);
8783 if (e_editor_page_get_convert_in_situ (editor_page
, &start_at_bottom
, &top_signature
)) {
8784 e_editor_dom_convert_content (editor_page
, NULL
, start_at_bottom
, top_signature
);
8785 /* The BODY could be replaced during the conversion */
8786 body
= webkit_dom_document_get_body (document
);
8787 /* Make the quote marks non-selectable. */
8788 e_editor_dom_disable_quote_marks_select (editor_page
);
8789 dom_set_links_active (document
, FALSE
);
8790 e_editor_page_set_convert_in_situ (editor_page
, FALSE
, -1, -1);
8792 /* The composer body could be empty in some case (loading an empty string
8793 * or empty HTML). In that case create the initial paragraph. */
8794 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
))) {
8795 WebKitDOMElement
*paragraph
;
8797 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
8798 webkit_dom_element_set_id (paragraph
, "-x-evo-input-start");
8799 webkit_dom_node_append_child (
8800 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (paragraph
), NULL
);
8801 e_editor_dom_selection_restore (editor_page
);
8806 WebKitDOMNodeList
*list
;
8809 list
= webkit_dom_document_query_selector_all (document
, "pre", NULL
);
8810 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
8811 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
), *parent
;
8812 WebKitDOMElement
*element
;
8815 element
= WEBKIT_DOM_ELEMENT (node
);
8816 parent
= webkit_dom_node_get_parent_node (node
);
8817 inner_html
= webkit_dom_element_get_inner_html (element
);
8819 if (inner_html
&& *inner_html
) {
8822 strv
= g_strsplit (inner_html
, "\n", -1);
8823 if (strv
&& strv
[0] && strv
[1]) {
8824 WebKitDOMElement
*pre
;
8827 for (jj
= 0; strv
[jj
]; jj
++) {
8828 pre
= webkit_dom_document_create_element (document
, "pre", NULL
);
8830 gint len
= strlen (strv
[jj
]);
8832 if (strv
[jj
][len
- 1] == '\r') {
8833 strv
[jj
][len
- 1] = '\0';
8838 webkit_dom_html_element_set_inner_html (WEBKIT_DOM_HTML_ELEMENT (pre
), strv
[jj
], NULL
);
8840 WebKitDOMElement
*br
;
8842 br
= webkit_dom_document_create_element (document
, "br", NULL
);
8843 webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre
), WEBKIT_DOM_NODE (br
), NULL
);
8846 webkit_dom_node_insert_before (parent
, WEBKIT_DOM_NODE (pre
), node
, NULL
);
8855 g_free (inner_html
);
8858 g_clear_object (&list
);
8861 adapt_to_editor_dom_changes (document
);
8863 /* Make the quote marks non-selectable. */
8864 e_editor_dom_disable_quote_marks_select (editor_page
);
8865 dom_set_links_active (document
, FALSE
);
8866 put_body_in_citation (document
);
8867 move_elements_to_body (editor_page
);
8868 repair_blockquotes (document
);
8869 remove_thunderbird_signature (document
);
8871 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body
), "data-evo-draft")) {
8872 /* Restore the selection how it was when the draft was saved */
8873 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (body
), FALSE
);
8874 e_editor_dom_selection_restore (editor_page
);
8875 e_editor_dom_remove_embedded_style_sheet (editor_page
);
8878 /* The composer body could be empty in some case (loading an empty string
8879 * or empty HTML. In that case create the initial paragraph. */
8880 if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
))) {
8881 WebKitDOMElement
*paragraph
;
8883 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
8884 webkit_dom_element_set_id (paragraph
, "-x-evo-input-start");
8885 webkit_dom_node_append_child (
8886 WEBKIT_DOM_NODE (body
), WEBKIT_DOM_NODE (paragraph
), NULL
);
8887 e_editor_dom_selection_restore (editor_page
);
8890 e_editor_dom_fix_file_uri_images (editor_page
);
8891 change_cid_images_src_to_base64 (editor_page
);
8894 /* Register on input event that is called when the content (body) is modified */
8895 e_editor_dom_register_input_event_listener_on_body (editor_page
);
8896 register_html_events_handlers (editor_page
, body
);
8898 if (e_editor_page_get_inline_spelling_enabled (editor_page
))
8899 e_editor_dom_force_spell_check_in_viewport (editor_page
);
8901 e_editor_dom_turn_spell_check_off (editor_page
);
8903 e_editor_dom_scroll_to_caret (editor_page
);
8905 dom_window
= webkit_dom_document_get_default_view (document
);
8907 webkit_dom_event_target_add_event_listener (
8908 WEBKIT_DOM_EVENT_TARGET (dom_window
),
8910 G_CALLBACK (body_scroll_event_cb
),
8914 /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the
8915 * callback won't be set up. */
8919 encode_to_base64_data (const gchar
*src_uri
,
8924 gchar
*filename
, *data
= NULL
;
8926 g_return_val_if_fail (src_uri
!= NULL
, NULL
);
8928 file
= g_file_new_for_uri (src_uri
);
8932 filename
= g_file_get_path (file
);
8934 g_object_unref (file
);
8938 info
= g_file_query_info (file
, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
"," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
,
8939 G_FILE_QUERY_INFO_NONE
, NULL
, NULL
);
8942 gchar
*mime_type
, *content
= NULL
;
8945 mime_type
= g_content_type_get_mime_type (g_file_info_get_content_type (info
));
8947 if (mime_type
&& g_file_get_contents (filename
, &content
, &length
, NULL
)) {
8948 gchar
*base64_encoded
;
8951 *data_name
= g_strdup (g_file_info_get_display_name (info
));
8953 base64_encoded
= g_base64_encode ((const guchar
*) content
, length
);
8954 data
= g_strconcat ("data:", mime_type
, ";base64,", base64_encoded
, NULL
);
8955 g_free (base64_encoded
);
8958 g_clear_object (&info
);
8963 g_clear_object (&file
);
8970 e_editor_dom_get_inline_images_data (EEditorPage
*editor_page
,
8971 const gchar
*uid_domain
)
8973 WebKitDOMDocument
*document
;
8974 WebKitDOMNodeList
*list
= NULL
;
8975 GVariant
*result
= NULL
;
8976 GVariantBuilder
*builder
= NULL
;
8977 GHashTable
*added
= NULL
;
8980 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
8982 document
= e_editor_page_get_document (editor_page
);
8983 list
= webkit_dom_document_query_selector_all (document
, "img[src]", NULL
);
8985 length
= webkit_dom_node_list_get_length (list
);
8987 g_clear_object (&list
);
8991 builder
= g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
8993 added
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
8994 for (ii
= length
; ii
--;) {
8997 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
8998 gchar
*src
= webkit_dom_element_get_attribute (
8999 WEBKIT_DOM_ELEMENT (node
), "src");
9004 if ((id
= g_hash_table_lookup (added
, src
)) != NULL
) {
9005 cid
= g_strdup_printf ("cid:%s", id
);
9006 } else if (g_ascii_strncasecmp (src
, "data:", 5) == 0) {
9007 gchar
*data_name
= webkit_dom_element_get_attribute (
9008 WEBKIT_DOM_ELEMENT (node
), "data-name");
9013 new_id
= camel_header_msgid_generate (uid_domain
);
9014 g_variant_builder_add (
9015 builder
, "(sss)", src
, data_name
, new_id
);
9016 cid
= g_strdup_printf ("cid:%s", new_id
);
9018 g_hash_table_insert (added
, g_strdup (src
), new_id
);
9020 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-inline", "", NULL
);
9023 } else if (g_ascii_strncasecmp (src
, "file://", 7) == 0) {
9024 gchar
*data
, *data_name
= NULL
;
9026 data
= encode_to_base64_data (src
, &data_name
);
9028 if (data
&& data_name
) {
9031 new_id
= camel_header_msgid_generate (uid_domain
);
9032 g_variant_builder_add (builder
, "(sss)", data
, data_name
, new_id
);
9033 cid
= g_strdup_printf ("cid:%s", new_id
);
9035 g_hash_table_insert (added
, data
, new_id
);
9037 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-name", data_name
, NULL
);
9038 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "data-inline", "", NULL
);
9047 webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node
), "src", cid
, NULL
);
9053 g_clear_object (&list
);
9056 list
= webkit_dom_document_query_selector_all (
9057 document
, "[data-inline][background]", NULL
);
9058 length
= webkit_dom_node_list_get_length (list
);
9062 builder
= g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
9064 added
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
9066 for (ii
= length
; ii
--;) {
9069 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
9070 gchar
*src
= webkit_dom_element_get_attribute (
9071 WEBKIT_DOM_ELEMENT (node
), "background");
9076 if ((id
= g_hash_table_lookup (added
, src
)) != NULL
) {
9077 cid
= g_strdup_printf ("cid:%s", id
);
9078 webkit_dom_element_set_attribute (
9079 WEBKIT_DOM_ELEMENT (node
), "background", cid
, NULL
);
9082 gchar
*data_name
= webkit_dom_element_get_attribute (
9083 WEBKIT_DOM_ELEMENT (node
), "data-name");
9088 new_id
= camel_header_msgid_generate (uid_domain
);
9089 g_variant_builder_add (
9090 builder
, "(sss)", src
, data_name
, new_id
);
9091 cid
= g_strdup_printf ("cid:%s", new_id
);
9093 g_hash_table_insert (added
, src
, new_id
);
9095 webkit_dom_element_set_attribute (
9096 WEBKIT_DOM_ELEMENT (node
), "background", cid
, NULL
);
9103 g_clear_object (&list
);
9105 g_hash_table_destroy (added
);
9108 result
= g_variant_new ("a(sss)", builder
);
9109 g_variant_builder_unref (builder
);
9116 pasting_quoted_content (const gchar
*content
)
9118 /* Check if the content we are pasting is a quoted content from composer.
9119 * If it is, we can't use WebKit to paste it as it would leave the formatting
9120 * on the content. */
9121 return g_str_has_prefix (
9123 "<meta http-equiv=\"content-type\" content=\"text/html; "
9124 "charset=utf-8\"><blockquote type=\"cite\"") &&
9125 strstr (content
, "\"-x-evo-");
9129 remove_apple_interchange_newline_elements (WebKitDOMDocument
*document
)
9132 WebKitDOMHTMLCollection
*collection
= NULL
;
9134 collection
= webkit_dom_document_get_elements_by_class_name_as_html_collection (
9135 document
, "Apple-interchange-newline");
9136 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;) {
9137 WebKitDOMNode
*node
= webkit_dom_html_collection_item (collection
, ii
);
9141 g_clear_object (&collection
);
9145 * e_editor_dom_insert_html:
9146 * @selection: an #EEditorSelection
9147 * @html_text: an HTML code to insert
9149 * Insert @html_text into document at current cursor position. When a text range
9150 * is selected, it will be replaced by @html_text.
9153 e_editor_dom_insert_html (EEditorPage
*editor_page
,
9154 const gchar
*html_text
)
9156 EEditorHistoryEvent
*ev
= NULL
;
9157 EEditorUndoRedoManager
*manager
;
9158 gboolean html_mode
, undo_redo_in_progress
;
9159 WebKitDOMDocument
*document
;
9160 WebKitDOMNode
*block
= NULL
;
9162 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
9163 g_return_if_fail (html_text
!= NULL
);
9165 document
= e_editor_page_get_document (editor_page
);
9167 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9168 undo_redo_in_progress
= e_editor_undo_redo_manager_is_operation_in_progress (manager
);
9169 if (!undo_redo_in_progress
) {
9172 ev
= g_new0 (EEditorHistoryEvent
, 1);
9173 ev
->type
= HISTORY_INSERT_HTML
;
9175 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
9176 e_editor_dom_selection_get_coordinates (editor_page
,
9177 &ev
->before
.start
.x
,
9178 &ev
->before
.start
.y
,
9183 ev
->before
.end
.x
= ev
->before
.start
.x
;
9184 ev
->before
.end
.y
= ev
->before
.start
.y
;
9187 ev
->data
.string
.from
= NULL
;
9188 ev
->data
.string
.to
= g_strdup (html_text
);
9191 html_mode
= e_editor_page_get_html_mode (editor_page
);
9193 (e_editor_page_is_pasting_content_from_itself (editor_page
) &&
9194 !pasting_quoted_content (html_text
))) {
9195 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
9196 EEditorHistoryEvent
*event
;
9197 WebKitDOMDocumentFragment
*fragment
;
9198 WebKitDOMRange
*range
= NULL
;
9200 event
= g_new0 (EEditorHistoryEvent
, 1);
9201 event
->type
= HISTORY_DELETE
;
9203 range
= e_editor_dom_get_current_range (editor_page
);
9204 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
9205 g_clear_object (&range
);
9206 event
->data
.fragment
= g_object_ref (fragment
);
9208 e_editor_dom_selection_get_coordinates (editor_page
,
9209 &event
->before
.start
.x
,
9210 &event
->before
.start
.y
,
9211 &event
->before
.end
.x
,
9212 &event
->before
.end
.y
);
9214 event
->after
.start
.x
= event
->before
.start
.x
;
9215 event
->after
.start
.y
= event
->before
.start
.y
;
9216 event
->after
.end
.x
= event
->before
.start
.x
;
9217 event
->after
.end
.y
= event
->before
.start
.y
;
9219 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
9221 event
= g_new0 (EEditorHistoryEvent
, 1);
9222 event
->type
= HISTORY_AND
;
9224 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
9226 WebKitDOMElement
*selection_marker
;
9228 e_editor_dom_selection_save (editor_page
);
9230 /* If current block contains just the BR element, remove
9231 * it otherwise WebKit will create a new block (with
9232 * text node that will contain '\n') on the end of inserted
9233 * content. Also remember the block and remove it if it's
9234 * empty after we insert the content. */
9235 selection_marker
= webkit_dom_document_get_element_by_id (
9236 document
, "-x-evo-selection-start-marker");
9238 if (!e_editor_page_is_pasting_content_from_itself (editor_page
)) {
9239 if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker
))) {
9240 WebKitDOMNode
*sibling
;
9242 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker
));
9243 sibling
= webkit_dom_node_get_next_sibling (sibling
);
9244 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
))
9245 remove_node (sibling
);
9248 block
= e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_marker
));
9250 e_editor_dom_selection_restore (editor_page
);
9253 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_HTML
, html_text
);
9256 remove_node_if_empty (block
);
9258 e_editor_dom_fix_file_uri_images (editor_page
);
9260 if (strstr (html_text
, "id=\"-x-evo-selection-start-marker\""))
9261 e_editor_dom_selection_restore (editor_page
);
9263 e_editor_dom_check_magic_links (editor_page
, FALSE
);
9264 e_editor_dom_scroll_to_caret (editor_page
);
9265 e_editor_dom_force_spell_check_in_viewport (editor_page
);
9267 /* Don't save history in the underlying function. */
9268 if (!undo_redo_in_progress
)
9269 e_editor_undo_redo_manager_set_operation_in_progress (manager
, TRUE
);
9270 e_editor_dom_convert_and_insert_html_into_selection (editor_page
, html_text
, TRUE
);
9271 if (!undo_redo_in_progress
)
9272 e_editor_undo_redo_manager_set_operation_in_progress (manager
, FALSE
);
9275 remove_apple_interchange_newline_elements (document
);
9278 e_editor_dom_selection_get_coordinates (editor_page
,
9284 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9289 save_history_for_delete_or_backspace (EEditorPage
*editor_page
,
9290 gboolean delete_key
,
9291 gboolean control_key
)
9293 WebKitDOMDocument
*document
;
9294 WebKitDOMDocumentFragment
*fragment
= NULL
;
9295 WebKitDOMDOMWindow
*dom_window
= NULL
;
9296 WebKitDOMDOMSelection
*dom_selection
= NULL
;
9297 WebKitDOMRange
*range
= NULL
;
9298 EEditorHistoryEvent
*ev
= NULL
;
9299 EEditorUndoRedoManager
*manager
;
9301 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
9303 document
= e_editor_page_get_document (editor_page
);
9304 dom_window
= webkit_dom_document_get_default_view (document
);
9305 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
9306 g_clear_object (&dom_window
);
9308 if (!webkit_dom_dom_selection_get_range_count (dom_selection
)) {
9309 g_clear_object (&dom_selection
);
9313 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9315 /* Check if we can delete something */
9316 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
9317 WebKitDOMRange
*tmp_range
= NULL
;
9319 webkit_dom_dom_selection_modify (
9320 dom_selection
, "move", delete_key
? "right" : "left", "character");
9322 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9323 if (webkit_dom_range_compare_boundary_points (tmp_range
, WEBKIT_DOM_RANGE_END_TO_END
, range
, NULL
) == 0) {
9324 g_clear_object (&dom_selection
);
9325 g_clear_object (&range
);
9326 g_clear_object (&tmp_range
);
9331 webkit_dom_dom_selection_modify (
9332 dom_selection
, "move", delete_key
? "left" : "right", "character");
9334 g_clear_object (&tmp_range
);
9337 if (save_history_before_event_in_table (editor_page
, range
)) {
9338 g_clear_object (&range
);
9339 g_clear_object (&dom_selection
);
9343 ev
= g_new0 (EEditorHistoryEvent
, 1);
9344 ev
->type
= HISTORY_DELETE
;
9346 e_editor_dom_selection_save (editor_page
);
9348 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
9349 g_clear_object (&range
);
9350 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9352 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
9353 gboolean removing_from_anchor
= FALSE
;
9354 WebKitDOMRange
*range_clone
= NULL
;
9355 WebKitDOMNode
*node
;
9357 e_editor_page_block_selection_changed (editor_page
);
9359 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
9361 WebKitDOMRange
*tmp_range
= NULL
;
9363 /* Control + Delete/Backspace deletes previous/next word. */
9364 webkit_dom_dom_selection_modify (
9365 dom_selection
, "move", delete_key
? "right" : "left", "word");
9366 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9368 webkit_dom_range_set_end (
9370 webkit_dom_range_get_end_container (tmp_range
, NULL
),
9371 webkit_dom_range_get_end_offset (tmp_range
, NULL
),
9374 webkit_dom_range_set_start (
9376 webkit_dom_range_get_start_container (tmp_range
, NULL
),
9377 webkit_dom_range_get_start_offset (tmp_range
, NULL
),
9379 g_clear_object (&tmp_range
);
9381 typedef WebKitDOMNode
* (*GetSibling
)(WebKitDOMNode
*node
);
9382 WebKitDOMNode
*container
, *sibling
;
9383 WebKitDOMElement
*selection_marker
;
9385 GetSibling get_sibling
= delete_key
?
9386 webkit_dom_node_get_next_sibling
:
9387 webkit_dom_node_get_previous_sibling
;
9389 container
= webkit_dom_range_get_end_container (range_clone
, NULL
);
9390 sibling
= get_sibling (container
);
9392 selection_marker
= webkit_dom_document_get_element_by_id (
9395 "-x-evo-selection-end-marker" :
9396 "-x-evo-selection-start-marker");
9398 if (selection_marker
) {
9399 WebKitDOMNode
*tmp_sibling
;
9401 tmp_sibling
= get_sibling (WEBKIT_DOM_NODE (selection_marker
));
9402 if (!tmp_sibling
|| (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp_sibling
) &&
9403 !element_has_class (WEBKIT_DOM_ELEMENT (tmp_sibling
), "-x-evo-wrap-br")))
9404 sibling
= WEBKIT_DOM_NODE (selection_marker
);
9407 if (e_editor_dom_is_selection_position_node (sibling
)) {
9408 if ((node
= get_sibling (sibling
)))
9409 node
= get_sibling (node
);
9411 if (WEBKIT_DOM_IS_ELEMENT (node
) &&
9412 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node
), "data-hidden-space")) {
9413 fragment
= webkit_dom_document_create_document_fragment (document
);
9414 webkit_dom_node_append_child (
9415 WEBKIT_DOM_NODE (fragment
),
9417 webkit_dom_document_create_text_node (document
, " ")),
9419 } else if (delete_key
) {
9420 webkit_dom_range_set_start (
9421 range_clone
, node
, 0, NULL
);
9422 webkit_dom_range_set_end (
9423 range_clone
, node
, 1, NULL
);
9426 WebKitDOMRange
*tmp_range
= NULL
, *actual_range
= NULL
;
9428 actual_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9430 webkit_dom_dom_selection_modify (
9431 dom_selection
, "move", delete_key
? "right" : "left", "character");
9433 tmp_range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
9434 if (webkit_dom_range_compare_boundary_points (tmp_range
, WEBKIT_DOM_RANGE_END_TO_END
, actual_range
, NULL
) != 0) {
9435 WebKitDOMNode
*actual_block
;
9436 WebKitDOMNode
*tmp_block
;
9438 actual_block
= e_editor_dom_get_parent_block_node_from_child (container
);
9440 tmp_block
= delete_key
?
9441 webkit_dom_range_get_end_container (tmp_range
, NULL
) :
9442 webkit_dom_range_get_start_container (tmp_range
, NULL
);
9443 tmp_block
= e_editor_dom_get_parent_block_node_from_child (tmp_block
);
9445 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9446 webkit_dom_dom_selection_add_range (dom_selection
, actual_range
);
9449 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (actual_block
))
9450 actual_block
= webkit_dom_node_get_parent_node (actual_block
);
9452 fragment
= webkit_dom_document_create_document_fragment (document
);
9454 WebKitDOMNode
*clone
;
9456 clone
= webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
);
9457 if (e_editor_dom_get_citation_level (actual_block
) > 0)
9458 webkit_dom_element_set_attribute (
9459 WEBKIT_DOM_ELEMENT (clone
),
9463 webkit_dom_node_append_child (
9464 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9466 clone
= webkit_dom_node_clone_node_with_error (tmp_block
, TRUE
, NULL
);
9467 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9468 webkit_dom_element_set_attribute (
9469 WEBKIT_DOM_ELEMENT (clone
),
9473 webkit_dom_node_append_child (
9474 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9476 WebKitDOMNode
*clone
;
9478 clone
= webkit_dom_node_clone_node_with_error (tmp_block
, TRUE
, NULL
);
9479 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9480 webkit_dom_element_set_attribute (
9481 WEBKIT_DOM_ELEMENT (clone
),
9485 webkit_dom_node_append_child (
9486 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9488 clone
= webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
);
9489 if (e_editor_dom_get_citation_level (tmp_block
) > 0)
9490 webkit_dom_element_set_attribute (
9491 WEBKIT_DOM_ELEMENT (clone
),
9495 webkit_dom_node_append_child (
9496 WEBKIT_DOM_NODE (fragment
), clone
, NULL
);
9499 G_OBJECT (fragment
),
9500 "history-concatenating-blocks",
9501 GINT_TO_POINTER (1));
9504 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9505 webkit_dom_dom_selection_add_range (dom_selection
, actual_range
);
9507 g_clear_object (&tmp_range
);
9508 g_clear_object (&actual_range
);
9513 /* FIXME This code is wrong for unicode smileys. */
9514 offset
= webkit_dom_range_get_start_offset (range_clone
, NULL
);
9517 webkit_dom_range_set_end (
9518 range_clone
, container
, offset
+ 1, NULL
);
9520 webkit_dom_range_set_start (
9521 range_clone
, container
, offset
- 1, NULL
);
9523 removing_from_anchor
= WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (
9524 webkit_dom_node_get_parent_node (container
));
9529 fragment
= webkit_dom_range_clone_contents (range_clone
, NULL
);
9530 if (removing_from_anchor
)
9532 G_OBJECT (fragment
),
9533 "history-removing-from-anchor",
9534 GINT_TO_POINTER (1));
9535 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
9538 e_editor_page_unblock_selection_changed (editor_page
);
9539 g_clear_object (&range
);
9540 g_clear_object (&range_clone
);
9541 g_clear_object (&dom_selection
);
9542 g_warning ("History event was not saved for %s key", delete_key
? "Delete" : "Backspace");
9543 e_editor_dom_selection_restore (editor_page
);
9549 ev
->after
.start
.x
= ev
->before
.start
.x
;
9550 ev
->after
.start
.y
= ev
->before
.start
.y
;
9551 ev
->after
.end
.x
= ev
->before
.end
.x
;
9552 ev
->after
.end
.y
= ev
->before
.end
.y
;
9554 webkit_dom_range_collapse (range_clone
, TRUE
, NULL
);
9555 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9556 webkit_dom_dom_selection_add_range (dom_selection
, range_clone
);
9558 gboolean selection_saved
= FALSE
;
9559 WebKitDOMRange
*tmp_range
= NULL
;
9561 if (webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker"))
9562 selection_saved
= TRUE
;
9564 if (selection_saved
)
9565 e_editor_dom_selection_restore (editor_page
);
9567 tmp_range
= webkit_dom_range_clone_range (range_clone
, NULL
);
9568 /* Prepare the selection to the right position after
9569 * delete and save it. */
9570 webkit_dom_range_collapse (range_clone
, TRUE
, NULL
);
9571 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9572 webkit_dom_dom_selection_add_range (dom_selection
, range_clone
);
9573 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9574 /* Restore the selection where it was before the
9575 * history event was saved. */
9576 webkit_dom_range_collapse (tmp_range
, FALSE
, NULL
);
9577 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
9578 webkit_dom_dom_selection_add_range (dom_selection
, tmp_range
);
9579 g_clear_object (&tmp_range
);
9581 if (selection_saved
)
9582 e_editor_dom_selection_save (editor_page
);
9585 gboolean selection_saved
= FALSE
;
9587 if (webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-start-marker"))
9588 selection_saved
= TRUE
;
9590 if (selection_saved
)
9591 e_editor_dom_selection_restore (editor_page
);
9594 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9596 webkit_dom_dom_selection_modify (dom_selection
, "move", "left", "character");
9597 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9598 webkit_dom_dom_selection_modify (dom_selection
, "move", "right", "character");
9600 ev
->after
.end
.x
= ev
->after
.start
.x
;
9601 ev
->after
.end
.y
= ev
->after
.start
.y
;
9604 if (selection_saved
)
9605 e_editor_dom_selection_save (editor_page
);
9608 g_clear_object (&range_clone
);
9611 if (!WEBKIT_DOM_IS_ELEMENT (node
)) {
9612 webkit_dom_node_insert_before (
9613 WEBKIT_DOM_NODE (fragment
),
9615 dom_create_selection_marker (document
, FALSE
)),
9616 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
9618 webkit_dom_node_insert_before (
9619 WEBKIT_DOM_NODE (fragment
),
9621 dom_create_selection_marker (document
, TRUE
)),
9622 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
9626 if (!WEBKIT_DOM_IS_ELEMENT (node
)) {
9627 webkit_dom_node_append_child (
9628 WEBKIT_DOM_NODE (fragment
),
9630 dom_create_selection_marker (document
, TRUE
)),
9632 webkit_dom_node_append_child (
9633 WEBKIT_DOM_NODE (fragment
),
9635 dom_create_selection_marker (document
, FALSE
)),
9640 e_editor_page_unblock_selection_changed (editor_page
);
9642 WebKitDOMElement
*tmp_element
;
9643 WebKitDOMNode
*sibling
;
9645 ev
->after
.start
.x
= ev
->before
.start
.x
;
9646 ev
->after
.start
.y
= ev
->before
.start
.y
;
9647 ev
->after
.end
.x
= ev
->before
.start
.x
;
9648 ev
->after
.end
.y
= ev
->before
.start
.y
;
9650 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
9652 tmp_element
= webkit_dom_document_fragment_query_selector (
9653 fragment
, "#-x-evo-selection-start-marker", NULL
);
9655 remove_node (WEBKIT_DOM_NODE (tmp_element
));
9657 tmp_element
= webkit_dom_document_fragment_query_selector (
9658 fragment
, "#-x-evo-selection-end-marker", NULL
);
9660 remove_node (WEBKIT_DOM_NODE (tmp_element
));
9662 remove_empty_blocks (document
);
9664 /* Selection starts in the beginning of blockquote. */
9665 tmp_element
= webkit_dom_document_get_element_by_id (
9666 document
, "-x-evo-selection-start-marker");
9667 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element
));
9668 if (sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
9669 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted")) {
9670 WebKitDOMNode
*child
;
9672 tmp_element
= webkit_dom_document_get_element_by_id (
9673 document
, "-x-evo-selection-end-marker");
9675 /* If there is no text after the selection end it means that
9676 * the block will be replaced with block that is body's descendant
9677 * and not the blockquote's one. Also if the selection started
9678 * in the beginning of blockquote we have to insert the quote
9679 * characters into the deleted content to correctly restore
9680 * them during undo/redo operations. */
9681 if (!(tmp_element
&& webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element
)))) {
9682 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
9683 while (child
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child
))
9684 child
= webkit_dom_node_get_first_child (child
);
9686 child
= webkit_dom_node_get_first_child (child
);
9687 if (child
&& (WEBKIT_DOM_IS_TEXT (child
) ||
9688 (WEBKIT_DOM_IS_ELEMENT (child
) &&
9689 !element_has_class (WEBKIT_DOM_ELEMENT (child
), "-x-evo-quoted")))) {
9690 webkit_dom_node_insert_before (
9691 webkit_dom_node_get_parent_node (child
),
9692 webkit_dom_node_clone_node_with_error (sibling
, TRUE
, NULL
),
9699 /* When we were cloning the range above and the range contained
9700 * quoted content there will still be blockquote missing in the
9701 * final range. Let's modify the fragment and add it there. */
9702 tmp_element
= webkit_dom_document_get_element_by_id (
9703 document
, "-x-evo-selection-end-marker");
9705 WebKitDOMNode
*node
;
9707 node
= WEBKIT_DOM_NODE (tmp_element
);
9708 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node
)))
9709 node
= webkit_dom_node_get_parent_node (node
);
9711 if (node
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node
)) {
9712 WebKitDOMNode
*last_child
;
9714 last_child
= webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment
));
9716 if (last_child
&& !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child
)) {
9717 WebKitDOMDocumentFragment
*tmp_fragment
;
9718 WebKitDOMNode
*clone
;
9720 tmp_fragment
= webkit_dom_document_create_document_fragment (document
);
9721 clone
= webkit_dom_node_clone_node_with_error (node
, FALSE
, NULL
);
9722 clone
= webkit_dom_node_append_child (
9723 WEBKIT_DOM_NODE (tmp_fragment
), clone
, NULL
);
9724 webkit_dom_node_append_child (clone
, WEBKIT_DOM_NODE (fragment
), NULL
);
9725 fragment
= tmp_fragment
;
9730 /* FIXME Ugly hack */
9731 /* If the deleted selection contained the signature (or at least its
9732 * part) replace it with the unchanged signature to correctly perform
9733 * undo operation. */
9734 tmp_element
= webkit_dom_document_fragment_query_selector (fragment
, ".-x-evo-signature-wrapper", NULL
);
9736 WebKitDOMElement
*signature
;
9738 signature
= webkit_dom_document_query_selector (document
, ".-x-evo-signature-wrapper", NULL
);
9740 webkit_dom_node_replace_child (
9741 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp_element
)),
9742 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (signature
), TRUE
, NULL
),
9743 WEBKIT_DOM_NODE (tmp_element
),
9749 g_clear_object (&range
);
9750 g_clear_object (&dom_selection
);
9752 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (delete_key
));
9753 g_object_set_data (G_OBJECT (fragment
), "history-control-key", GINT_TO_POINTER (control_key
));
9755 ev
->data
.fragment
= g_object_ref (fragment
);
9757 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9758 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9760 e_editor_dom_selection_restore (editor_page
);
9764 e_editor_dom_fix_structure_after_delete_before_quoted_content (EEditorPage
*editor_page
,
9766 gboolean control_key
,
9767 gboolean delete_key
)
9769 WebKitDOMDocument
*document
;
9770 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
9771 WebKitDOMNode
*block
, *node
;
9772 gboolean collapsed
= FALSE
;
9774 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9776 document
= e_editor_page_get_document (editor_page
);
9777 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
9779 e_editor_dom_selection_save (editor_page
);
9781 selection_start_marker
= webkit_dom_document_get_element_by_id (
9782 document
, "-x-evo-selection-start-marker");
9783 selection_end_marker
= webkit_dom_document_get_element_by_id (
9784 document
, "-x-evo-selection-end-marker");
9786 if (!selection_start_marker
|| !selection_end_marker
)
9790 WebKitDOMNode
*next_block
;
9792 block
= e_editor_dom_get_parent_block_node_from_child (
9793 WEBKIT_DOM_NODE (selection_start_marker
));
9795 next_block
= webkit_dom_node_get_next_sibling (block
);
9797 /* Next block is quoted content */
9798 if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block
)) {
9799 e_editor_dom_selection_restore (editor_page
);
9803 /* Delete was pressed in block without any content */
9804 if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
))) {
9805 e_editor_dom_selection_restore (editor_page
);
9809 /* If there is just BR element go ahead */
9810 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker
));
9811 if (node
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
9812 e_editor_dom_selection_restore (editor_page
);
9815 if (key_code
!= ~0) {
9816 e_editor_dom_selection_restore (editor_page
);
9817 save_history_for_delete_or_backspace (
9818 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
9820 e_editor_dom_selection_restore (editor_page
);
9822 /* Remove the empty block and move caret to the right place. */
9823 remove_node (block
);
9826 /* To the beginning of the next block. */
9827 while (next_block
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block
))
9828 next_block
= webkit_dom_node_get_first_child (next_block
);
9830 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-quoted"))
9831 next_block
= webkit_dom_node_get_next_sibling (next_block
);
9833 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (next_block
), TRUE
);
9835 WebKitDOMNode
*prev_block
;
9837 /* On the end of previous block. */
9838 prev_block
= webkit_dom_node_get_previous_sibling (next_block
);
9839 while (prev_block
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block
))
9840 prev_block
= webkit_dom_node_get_last_child (prev_block
);
9843 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (prev_block
), FALSE
);
9850 e_editor_dom_selection_restore (editor_page
);
9856 split_citation (EEditorPage
*editor_page
)
9858 WebKitDOMDocument
*document
;
9859 WebKitDOMElement
*element
;
9860 EEditorHistoryEvent
*ev
= NULL
;
9861 EEditorUndoRedoManager
*manager
;
9863 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9865 document
= e_editor_page_get_document (editor_page
);
9866 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
9868 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
9869 WebKitDOMElement
*selection_end
;
9870 WebKitDOMNode
*sibling
;
9872 ev
= g_new0 (EEditorHistoryEvent
, 1);
9873 ev
->type
= HISTORY_CITATION_SPLIT
;
9875 e_editor_dom_selection_save (editor_page
);
9877 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->before
.start
.x
, &ev
->before
.start
.y
, &ev
->before
.end
.x
, &ev
->before
.end
.y
);
9879 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
9880 WebKitDOMRange
*range
= NULL
;
9882 range
= e_editor_dom_get_current_range (editor_page
);
9883 insert_delete_event (editor_page
, range
);
9885 g_clear_object (&range
);
9887 ev
->before
.end
.x
= ev
->before
.start
.x
;
9888 ev
->before
.end
.y
= ev
->before
.start
.y
;
9891 selection_end
= webkit_dom_document_get_element_by_id (
9892 document
, "-x-evo-selection-end-marker");
9894 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end
));
9895 if (!sibling
|| (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
9896 !element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-wrap-br"))) {
9897 WebKitDOMDocumentFragment
*fragment
;
9899 fragment
= webkit_dom_document_create_document_fragment (document
);
9900 ev
->data
.fragment
= g_object_ref (fragment
);
9902 ev
->data
.fragment
= NULL
;
9904 e_editor_dom_selection_restore (editor_page
);
9907 element
= e_editor_dom_insert_new_line_into_citation (editor_page
, "");
9910 e_editor_dom_selection_get_coordinates (editor_page
, &ev
->after
.start
.x
, &ev
->after
.start
.y
, &ev
->after
.end
.x
, &ev
->after
.end
.y
);
9912 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
9915 return element
!= NULL
;
9919 delete_last_character_from_previous_line_in_quoted_block (EEditorPage
*editor_page
,
9923 WebKitDOMDocument
*document
;
9924 WebKitDOMDocumentFragment
*fragment
= NULL
;
9925 WebKitDOMElement
*element
;
9926 WebKitDOMNode
*node
, *beginning
, *prev_sibling
;
9927 EEditorHistoryEvent
*ev
= NULL
;
9928 gboolean hidden_space
= FALSE
;
9930 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
9932 /* We have to be in quoted content. */
9933 if (!e_editor_dom_selection_is_citation (editor_page
))
9936 /* Selection is just caret. */
9937 if (!e_editor_dom_selection_is_collapsed (editor_page
))
9940 document
= e_editor_page_get_document (editor_page
);
9942 e_editor_dom_selection_save (editor_page
);
9944 element
= webkit_dom_document_get_element_by_id (
9945 document
, "-x-evo-selection-start-marker");
9947 /* Before the caret are just quote characters */
9948 beginning
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
9949 if (!(beginning
&& WEBKIT_DOM_IS_ELEMENT (beginning
))) {
9950 WebKitDOMNode
*parent
;
9952 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
9953 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
))
9954 beginning
= webkit_dom_node_get_previous_sibling (parent
);
9959 /* Before the text is the beginning of line. */
9960 if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning
), "-x-evo-quoted")))
9963 /* If we are just on the beginning of the line and not on the beginning of
9964 * the block we need to remove the last character ourselves as well, otherwise
9965 * WebKit will put the caret to wrong position. */
9966 if (!(prev_sibling
= webkit_dom_node_get_previous_sibling (beginning
)))
9969 if (key_code
!= ~0) {
9970 ev
= g_new0 (EEditorHistoryEvent
, 1);
9971 ev
->type
= HISTORY_DELETE
;
9973 e_editor_dom_selection_get_coordinates (editor_page
,
9974 &ev
->before
.start
.x
,
9975 &ev
->before
.start
.y
,
9979 fragment
= webkit_dom_document_create_document_fragment (document
);
9982 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
)) {
9984 webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment
), prev_sibling
, NULL
);
9986 remove_node (prev_sibling
);
9989 prev_sibling
= webkit_dom_node_get_previous_sibling (beginning
);
9990 if (WEBKIT_DOM_IS_ELEMENT (prev_sibling
) &&
9991 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (prev_sibling
), "data-hidden-space")) {
9992 hidden_space
= TRUE
;
9994 webkit_dom_node_insert_before (
9995 WEBKIT_DOM_NODE (fragment
),
9997 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
)),
10000 remove_node (prev_sibling
);
10003 node
= webkit_dom_node_get_previous_sibling (beginning
);
10005 if (key_code
!= ~0)
10006 webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment
), beginning
, NULL
);
10008 remove_node (beginning
);
10010 if (!hidden_space
) {
10011 if (key_code
!= ~0) {
10014 data
= webkit_dom_character_data_substring_data (
10015 WEBKIT_DOM_CHARACTER_DATA (node
),
10016 webkit_dom_character_data_get_length (
10017 WEBKIT_DOM_CHARACTER_DATA (node
)) -1,
10021 webkit_dom_node_append_child (
10022 WEBKIT_DOM_NODE (fragment
),
10024 webkit_dom_document_create_text_node (document
, data
)),
10030 webkit_dom_character_data_delete_data (
10031 WEBKIT_DOM_CHARACTER_DATA (node
),
10032 webkit_dom_character_data_get_length (
10033 WEBKIT_DOM_CHARACTER_DATA (node
)) -1,
10038 if (key_code
!= ~0) {
10039 EEditorUndoRedoManager
*manager
;
10041 e_editor_dom_selection_get_coordinates (editor_page
,
10042 &ev
->after
.start
.x
,
10043 &ev
->after
.start
.y
,
10047 ev
->data
.fragment
= g_object_ref (fragment
);
10049 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10050 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10053 e_editor_dom_selection_restore (editor_page
);
10057 e_editor_dom_selection_restore (editor_page
);
10063 e_editor_dom_delete_last_character_on_line_in_quoted_block (EEditorPage
*editor_page
,
10065 gboolean control_key
)
10067 WebKitDOMDocument
*document
;
10068 WebKitDOMElement
*element
;
10069 WebKitDOMNode
*node
, *beginning
, *next_sibling
;
10070 gboolean success
= FALSE
;
10072 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10074 document
= e_editor_page_get_document (editor_page
);
10076 /* We have to be in quoted content. */
10077 if (!e_editor_dom_selection_is_citation (editor_page
))
10080 /* Selection is just caret. */
10081 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10084 e_editor_dom_selection_save (editor_page
);
10086 element
= webkit_dom_document_get_element_by_id (
10087 document
, "-x-evo-selection-start-marker");
10089 /* selection end marker */
10090 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
10092 /* We have to be on the end of line. */
10093 next_sibling
= webkit_dom_node_get_next_sibling (node
);
10094 if (next_sibling
&&
10095 (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
) ||
10096 webkit_dom_node_get_next_sibling (next_sibling
)))
10099 /* Before the caret is just text. */
10100 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10101 if (!(node
&& WEBKIT_DOM_IS_TEXT (node
)))
10104 /* There is just one character. */
10105 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
)) != 1)
10108 beginning
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (node
));
10109 if (!(beginning
&& WEBKIT_DOM_IS_ELEMENT (beginning
)))
10112 /* Before the text is the beginning of line. */
10113 if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning
), "-x-evo-quoted")))
10116 if (!webkit_dom_node_get_previous_sibling (beginning
))
10119 if (key_code
!= ~0) {
10120 e_editor_dom_selection_restore (editor_page
);
10121 save_history_for_delete_or_backspace (
10122 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10123 e_editor_dom_selection_save (editor_page
);
10126 element
= webkit_dom_node_get_parent_element (beginning
);
10127 remove_node (WEBKIT_DOM_NODE (element
));
10131 e_editor_dom_selection_restore (editor_page
);
10134 e_editor_dom_insert_new_line_into_citation (editor_page
, NULL
);
10140 selection_is_in_empty_list_item (WebKitDOMNode
*selection_start_marker
)
10143 WebKitDOMNode
*sibling
;
10145 /* Selection needs to be collapsed. */
10146 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
10147 if (!e_editor_dom_is_selection_position_node (sibling
))
10150 /* After the selection end there could be just the BR element. */
10151 sibling
= webkit_dom_node_get_next_sibling (sibling
);
10152 if (sibling
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
))
10155 if (sibling
&& webkit_dom_node_get_next_sibling (sibling
))
10158 sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
10163 /* Only text node with the zero width space character is allowed. */
10164 if (!WEBKIT_DOM_IS_TEXT (sibling
))
10167 if (webkit_dom_node_get_previous_sibling (sibling
))
10170 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (sibling
)) != 1)
10173 text
= webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (sibling
));
10174 if (!(text
&& g_strcmp0 (text
, UNICODE_ZERO_WIDTH_SPACE
) == 0)) {
10185 return_pressed_in_image_wrapper (EEditorPage
*editor_page
)
10187 WebKitDOMDocument
*document
;
10188 WebKitDOMDocumentFragment
*fragment
;
10189 WebKitDOMElement
*selection_start_marker
;
10190 WebKitDOMNode
*parent
, *block
, *clone
;
10191 EEditorHistoryEvent
*ev
= NULL
;
10192 EEditorUndoRedoManager
*manager
;
10194 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10196 document
= e_editor_page_get_document (editor_page
);
10198 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10201 e_editor_dom_selection_save (editor_page
);
10203 selection_start_marker
= webkit_dom_document_get_element_by_id (
10204 document
, "-x-evo-selection-start-marker");
10206 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
10207 if (!element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-resizable-wrapper")) {
10208 e_editor_dom_selection_restore (editor_page
);
10212 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10214 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10215 ev
= g_new0 (EEditorHistoryEvent
, 1);
10216 ev
->type
= HISTORY_INPUT
;
10218 e_editor_dom_selection_get_coordinates (editor_page
,
10219 &ev
->before
.start
.x
,
10220 &ev
->before
.start
.y
,
10222 &ev
->before
.end
.y
);
10224 fragment
= webkit_dom_document_create_document_fragment (document
);
10226 g_object_set_data (
10227 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10230 block
= e_editor_dom_get_parent_block_node_from_child (
10231 WEBKIT_DOM_NODE (selection_start_marker
));
10233 clone
= webkit_dom_node_clone_node_with_error (block
, FALSE
, NULL
);
10234 webkit_dom_node_append_child (
10235 clone
, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)), NULL
);
10237 webkit_dom_node_insert_before (
10238 webkit_dom_node_get_parent_node (block
),
10244 webkit_dom_node_append_child (
10245 WEBKIT_DOM_NODE (fragment
),
10246 webkit_dom_node_clone_node_with_error (clone
, TRUE
, NULL
),
10249 e_editor_dom_selection_get_coordinates (editor_page
,
10250 &ev
->after
.start
.x
,
10251 &ev
->after
.start
.y
,
10255 ev
->data
.fragment
= g_object_ref (fragment
);
10257 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10260 e_editor_page_emit_content_changed (editor_page
);
10262 e_editor_dom_selection_restore (editor_page
);
10268 return_pressed_after_h_rule (EEditorPage
*editor_page
)
10270 WebKitDOMDocument
*document
;
10271 WebKitDOMDocumentFragment
*fragment
;
10272 WebKitDOMElement
*selection_marker
;
10273 WebKitDOMNode
*node
, *block
, *clone
, *hr
, *insert_before
= NULL
;
10274 EEditorHistoryEvent
*ev
= NULL
;
10275 EEditorUndoRedoManager
*manager
;
10277 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10279 document
= e_editor_page_get_document (editor_page
);
10281 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10284 e_editor_dom_selection_save (editor_page
);
10286 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10288 if (e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10289 selection_marker
= webkit_dom_document_get_element_by_id (
10290 document
, "-x-evo-selection-end-marker");
10292 hr
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker
));
10293 hr
= webkit_dom_node_get_next_sibling (hr
);
10294 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker
));
10295 if (!node
|| !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
) || !hr
||
10296 !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr
)) {
10297 e_editor_dom_selection_restore (editor_page
);
10301 insert_before
= webkit_dom_node_get_next_sibling (hr
);
10303 selection_marker
= webkit_dom_document_get_element_by_id (
10304 document
, "-x-evo-selection-start-marker");
10306 node
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker
));
10307 hr
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker
));
10308 if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
) ||
10309 !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr
)) {
10310 e_editor_dom_selection_restore (editor_page
);
10314 insert_before
= WEBKIT_DOM_NODE (selection_marker
);
10316 ev
= g_new0 (EEditorHistoryEvent
, 1);
10317 ev
->type
= HISTORY_INPUT
;
10319 e_editor_dom_selection_get_coordinates (editor_page
,
10320 &ev
->before
.start
.x
,
10321 &ev
->before
.start
.y
,
10323 &ev
->before
.end
.y
);
10325 fragment
= webkit_dom_document_create_document_fragment (document
);
10327 g_object_set_data (
10328 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10331 block
= webkit_dom_node_get_previous_sibling (hr
);
10333 clone
= webkit_dom_node_clone_node_with_error (block
, FALSE
, NULL
);
10335 webkit_dom_node_append_child (
10336 clone
, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)), NULL
);
10338 webkit_dom_node_insert_before (
10339 webkit_dom_node_get_parent_node (hr
), clone
, insert_before
, NULL
);
10341 dom_remove_selection_markers (document
);
10343 webkit_dom_node_append_child (
10344 WEBKIT_DOM_NODE (clone
),
10346 dom_create_selection_marker (document
, TRUE
)),
10348 webkit_dom_node_append_child (
10349 WEBKIT_DOM_NODE (clone
),
10351 dom_create_selection_marker (document
, FALSE
)),
10355 webkit_dom_node_append_child (
10356 WEBKIT_DOM_NODE (fragment
),
10357 webkit_dom_node_clone_node_with_error (clone
, TRUE
, NULL
),
10360 e_editor_dom_selection_get_coordinates (editor_page
,
10361 &ev
->after
.start
.x
,
10362 &ev
->after
.start
.y
,
10366 ev
->data
.fragment
= g_object_ref (fragment
);
10368 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10371 e_editor_page_emit_content_changed (editor_page
);
10373 e_editor_dom_selection_restore (editor_page
);
10379 e_editor_dom_return_pressed_in_empty_list_item (EEditorPage
*editor_page
)
10381 WebKitDOMDocument
*document
;
10382 WebKitDOMElement
*selection_start_marker
;
10383 WebKitDOMNode
*parent
;
10385 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10387 document
= e_editor_page_get_document (editor_page
);
10389 if (!e_editor_dom_selection_is_collapsed (editor_page
))
10392 e_editor_dom_selection_save (editor_page
);
10394 selection_start_marker
= webkit_dom_document_get_element_by_id (
10395 document
, "-x-evo-selection-start-marker");
10397 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
10398 if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent
)) {
10399 e_editor_dom_selection_restore (editor_page
);
10403 if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start_marker
))) {
10404 EEditorHistoryEvent
*ev
= NULL
;
10405 EEditorUndoRedoManager
*manager
;
10406 WebKitDOMDocumentFragment
*fragment
;
10407 WebKitDOMElement
*paragraph
;
10408 WebKitDOMNode
*list
;
10410 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10412 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10413 ev
= g_new0 (EEditorHistoryEvent
, 1);
10414 ev
->type
= HISTORY_INPUT
;
10416 e_editor_dom_selection_get_coordinates (editor_page
,
10417 &ev
->before
.start
.x
,
10418 &ev
->before
.start
.y
,
10420 &ev
->before
.end
.y
);
10422 fragment
= webkit_dom_document_create_document_fragment (document
);
10424 g_object_set_data (
10425 G_OBJECT (fragment
), "history-return-key", GINT_TO_POINTER (1));
10428 list
= split_list_into_two (parent
, -1);
10431 webkit_dom_node_append_child (
10432 WEBKIT_DOM_NODE (fragment
),
10436 remove_node (parent
);
10439 paragraph
= e_editor_dom_prepare_paragraph (editor_page
, TRUE
);
10441 webkit_dom_node_insert_before (
10442 webkit_dom_node_get_parent_node (list
),
10443 WEBKIT_DOM_NODE (paragraph
),
10447 remove_node_if_empty (list
);
10450 e_editor_dom_selection_get_coordinates (editor_page
,
10451 &ev
->after
.start
.x
,
10452 &ev
->after
.start
.y
,
10456 ev
->data
.fragment
= g_object_ref (fragment
);
10458 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10461 e_editor_dom_selection_restore (editor_page
);
10463 e_editor_page_emit_content_changed (editor_page
);
10468 e_editor_dom_selection_restore (editor_page
);
10474 process_smiley_on_delete_or_backspace (EEditorPage
*editor_page
)
10476 WebKitDOMDocument
*document
;
10477 WebKitDOMElement
*element
;
10478 WebKitDOMNode
*parent
;
10479 gboolean in_smiley
= FALSE
;
10481 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
10483 document
= e_editor_page_get_document (editor_page
);
10484 e_editor_dom_selection_save (editor_page
);
10485 element
= webkit_dom_document_get_element_by_id (
10486 document
, "-x-evo-selection-start-marker");
10488 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
10489 if (WEBKIT_DOM_IS_ELEMENT (parent
) &&
10490 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-smiley-text"))
10493 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
10494 WebKitDOMNode
*prev_sibling
;
10496 prev_sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10497 if (prev_sibling
&& WEBKIT_DOM_IS_TEXT (prev_sibling
)) {
10498 gchar
*text
= webkit_dom_character_data_get_data (
10499 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
10501 if (g_strcmp0 (text
, UNICODE_ZERO_WIDTH_SPACE
) == 0) {
10502 WebKitDOMNode
*prev_prev_sibling
;
10504 prev_prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
10505 if (WEBKIT_DOM_IS_ELEMENT (prev_prev_sibling
) &&
10506 element_has_class (WEBKIT_DOM_ELEMENT (prev_prev_sibling
), "-x-evo-smiley-wrapper")) {
10507 remove_node (prev_sibling
);
10509 parent
= webkit_dom_node_get_last_child (prev_prev_sibling
);
10516 element
= webkit_dom_document_get_element_by_id (
10517 document
, "-x-evo-selection-end-marker");
10519 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
10520 if (WEBKIT_DOM_IS_ELEMENT (parent
) &&
10521 element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-smiley-text"))
10527 WebKitDOMNode
*wrapper
;
10529 wrapper
= webkit_dom_node_get_parent_node (parent
);
10530 if (!e_editor_page_get_html_mode (editor_page
)) {
10531 WebKitDOMNode
*child
;
10533 while ((child
= webkit_dom_node_get_first_child (parent
)))
10534 webkit_dom_node_insert_before (
10535 webkit_dom_node_get_parent_node (wrapper
),
10540 /* In the HTML mode the whole smiley will be removed. */
10541 remove_node (wrapper
);
10542 /* FIXME history will be probably broken here */
10545 e_editor_dom_selection_restore (editor_page
);
10549 e_editor_dom_key_press_event_process_return_key (EEditorPage
*editor_page
)
10551 WebKitDOMDocument
*document
;
10552 WebKitDOMNode
*table
= NULL
;
10553 gboolean first_cell
= FALSE
;
10555 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10557 document
= e_editor_page_get_document (editor_page
);
10558 /* Return pressed in the beginning of the first cell will insert
10559 * new block before the table (and move the caret there) if none
10560 * is already there, otherwise it will act as normal return. */
10561 if (selection_is_in_table (document
, &first_cell
, &table
) && first_cell
) {
10562 WebKitDOMNode
*node
;
10564 node
= webkit_dom_node_get_previous_sibling (table
);
10566 node
= webkit_dom_node_get_next_sibling (table
);
10567 node
= webkit_dom_node_clone_node_with_error (node
, FALSE
, NULL
);
10568 webkit_dom_node_append_child (
10570 WEBKIT_DOM_NODE (webkit_dom_document_create_element (
10571 document
, "br", NULL
)),
10573 dom_add_selection_markers_into_element_start (
10574 document
, WEBKIT_DOM_ELEMENT (node
), NULL
, NULL
);
10575 webkit_dom_node_insert_before (
10576 webkit_dom_node_get_parent_node (table
),
10580 e_editor_dom_selection_restore (editor_page
);
10581 e_editor_page_emit_content_changed (editor_page
);
10586 /* When user presses ENTER in a citation block, WebKit does
10587 * not break the citation automatically, so we need to use
10588 * the special command to do it. */
10589 if (e_editor_dom_selection_is_citation (editor_page
)) {
10590 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
10591 if (split_citation (editor_page
)) {
10592 e_editor_page_set_return_key_pressed (editor_page
, TRUE
);
10593 e_editor_dom_check_magic_links (editor_page
, FALSE
);
10594 e_editor_page_set_return_key_pressed (editor_page
, FALSE
);
10595 e_editor_page_emit_content_changed (editor_page
);
10602 /* If the ENTER key is pressed inside an empty list item then the list
10603 * is broken into two and empty paragraph is inserted between lists. */
10604 if (e_editor_dom_return_pressed_in_empty_list_item (editor_page
))
10607 if (return_pressed_in_image_wrapper (editor_page
))
10610 if (return_pressed_after_h_rule (editor_page
))
10617 remove_empty_bulleted_list_item (EEditorPage
*editor_page
)
10619 WebKitDOMDocument
*document
;
10620 WebKitDOMElement
*selection_start
;
10621 WebKitDOMNode
*parent
;
10622 EEditorUndoRedoManager
*manager
;
10624 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10626 document
= e_editor_page_get_document (editor_page
);
10627 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
10628 e_editor_dom_selection_save (editor_page
);
10630 selection_start
= webkit_dom_document_get_element_by_id (
10631 document
, "-x-evo-selection-start-marker");
10633 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start
));
10634 while (parent
&& !node_is_list_or_item (parent
))
10635 parent
= webkit_dom_node_get_parent_node (parent
);
10640 if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start
))) {
10641 EEditorHistoryEvent
*ev
= NULL
;
10642 WebKitDOMDocumentFragment
*fragment
;
10643 WebKitDOMNode
*prev_item
;
10645 prev_item
= webkit_dom_node_get_previous_sibling (parent
);
10647 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
10648 /* Insert new history event for Return to have the right coordinates.
10649 * The fragment will be added later. */
10650 ev
= g_new0 (EEditorHistoryEvent
, 1);
10651 ev
->type
= HISTORY_DELETE
;
10653 e_editor_dom_selection_get_coordinates (editor_page
,
10654 &ev
->before
.start
.x
,
10655 &ev
->before
.start
.y
,
10657 &ev
->before
.end
.y
);
10659 fragment
= webkit_dom_document_create_document_fragment (document
);
10664 webkit_dom_node_append_child (
10665 WEBKIT_DOM_NODE (fragment
),
10666 webkit_dom_node_clone_node_with_error (prev_item
, TRUE
, NULL
),
10669 webkit_dom_node_append_child (
10670 WEBKIT_DOM_NODE (fragment
),
10674 remove_node (parent
);
10677 dom_add_selection_markers_into_element_end (
10678 document
, WEBKIT_DOM_ELEMENT (prev_item
), NULL
, NULL
);
10681 e_editor_dom_selection_get_coordinates (editor_page
,
10682 &ev
->after
.start
.x
,
10683 &ev
->after
.start
.y
,
10687 ev
->data
.fragment
= g_object_ref (fragment
);
10689 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
10692 e_editor_page_emit_content_changed (editor_page
);
10693 e_editor_dom_selection_restore (editor_page
);
10698 e_editor_dom_selection_restore (editor_page
);
10704 e_editor_dom_key_press_event_process_backspace_key (EEditorPage
*editor_page
)
10706 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10708 /* BackSpace pressed in the beginning of quoted content changes
10709 * format to normal and inserts text into body */
10710 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
10711 e_editor_dom_selection_save (editor_page
);
10712 if (e_editor_dom_move_quoted_block_level_up (editor_page
) || delete_hidden_space (editor_page
)) {
10713 e_editor_dom_selection_restore (editor_page
);
10714 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
10715 e_editor_page_emit_content_changed (editor_page
);
10718 e_editor_dom_selection_restore (editor_page
);
10721 /* BackSpace in indented block decrease indent level by one */
10722 if (e_editor_dom_selection_is_indented (editor_page
) &&
10723 e_editor_dom_selection_is_collapsed (editor_page
)) {
10724 WebKitDOMDocument
*document
;
10725 WebKitDOMElement
*selection_start
;
10726 WebKitDOMNode
*prev_sibling
;
10728 document
= e_editor_page_get_document (editor_page
);
10730 e_editor_dom_selection_save (editor_page
);
10731 selection_start
= webkit_dom_document_get_element_by_id (
10732 document
, "-x-evo-selection-start-marker");
10734 /* Empty text node before caret */
10735 prev_sibling
= webkit_dom_node_get_previous_sibling (
10736 WEBKIT_DOM_NODE (selection_start
));
10737 if (prev_sibling
&& WEBKIT_DOM_IS_TEXT (prev_sibling
))
10738 if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (prev_sibling
)) == 0)
10739 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
10741 e_editor_dom_selection_restore (editor_page
);
10742 if (!prev_sibling
) {
10743 e_editor_dom_selection_unindent (editor_page
);
10744 e_editor_page_emit_content_changed (editor_page
);
10749 /* BackSpace pressed in an empty item in the bulleted list removes it. */
10750 if (!e_editor_page_get_html_mode (editor_page
) && e_editor_dom_selection_is_collapsed (editor_page
) &&
10751 remove_empty_bulleted_list_item (editor_page
))
10755 if (prevent_from_deleting_last_element_in_body (e_editor_page_get_document (editor_page
)))
10762 deleting_block_starting_in_quoted_content (EEditorPage
*editor_page
,
10764 gboolean control_key
)
10766 gint citation_level
;
10767 WebKitDOMDocument
*document
;
10768 WebKitDOMElement
*element
;
10769 WebKitDOMHTMLElement
*body
;
10770 WebKitDOMNode
*node
, *parent
, *block
, *sibling
;
10771 WebKitDOMRange
*range
= NULL
;
10773 e_editor_dom_selection_save (editor_page
);
10775 document
= e_editor_page_get_document (editor_page
);
10777 element
= webkit_dom_document_get_element_by_id (
10778 document
, "-x-evo-selection-start-marker");
10780 /* Before the caret are just quote characters */
10781 sibling
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
10782 if (!(sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
10783 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted"))) {
10787 if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (sibling
))) {
10791 element
= webkit_dom_document_get_element_by_id (
10792 document
, "-x-evo-selection-end-marker");
10794 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
10796 enable_quote_marks_select (document
);
10797 e_editor_dom_selection_restore (editor_page
);
10799 if (key_code
!= ~0)
10800 save_history_for_delete_or_backspace (
10801 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10803 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
10805 range
= e_editor_dom_get_current_range (editor_page
);
10806 node
= webkit_dom_range_get_end_container (range
, NULL
);
10808 block
= e_editor_dom_get_parent_block_node_from_child (node
);
10809 parent
= webkit_dom_node_get_parent_node (block
);
10810 body
= webkit_dom_document_get_body (document
);
10811 while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent
)))
10812 parent
= webkit_dom_node_get_parent_node (parent
);
10814 if (!citation_level
) {
10815 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
10816 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
10818 webkit_dom_node_insert_before (
10819 WEBKIT_DOM_NODE (body
),
10821 webkit_dom_node_get_next_sibling (parent
),
10824 WebKitDOMNode
*last_child
= webkit_dom_node_get_last_child (block
);
10826 if (WEBKIT_DOM_IS_ELEMENT (last_child
) &&
10827 element_has_class (WEBKIT_DOM_ELEMENT (last_child
), "-x-evo-quoted")) {
10828 webkit_dom_node_append_child (
10830 WEBKIT_DOM_NODE (webkit_dom_document_create_element (document
, "br", NULL
)),
10836 e_editor_dom_disable_quote_marks_select (editor_page
);
10837 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (block
), TRUE
);
10839 g_clear_object (&range
);
10843 e_editor_dom_selection_restore (editor_page
);
10849 e_editor_dom_key_press_event_process_delete_or_backspace_key (EEditorPage
*editor_page
,
10851 gboolean control_key
,
10854 WebKitDOMDocument
*document
;
10855 gboolean html_mode
, local_delete
, collapsed
;
10857 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
10859 document
= e_editor_page_get_document (editor_page
);
10860 html_mode
= e_editor_page_get_html_mode (editor_page
);
10861 local_delete
= (key_code
== HTML_KEY_CODE_DELETE
) || delete;
10863 if (e_editor_page_get_magic_smileys_enabled (editor_page
)) {
10864 /* If deleting something in a smiley it won't be a smiley
10865 * anymore (at least from Evolution' POV), so remove all
10866 * the elements that are hidden in the wrapper and leave
10867 * just the text. Also this ensures that when a smiley is
10868 * recognized and we press the BackSpace key we won't delete
10869 * the UNICODE_HIDDEN_SPACE, but we will correctly delete
10870 * the last character of smiley. */
10871 process_smiley_on_delete_or_backspace (editor_page
);
10874 if (!local_delete
&& !html_mode
&&
10875 e_editor_dom_delete_last_character_on_line_in_quoted_block (editor_page
, key_code
, control_key
))
10878 if (!local_delete
&& !html_mode
&&
10879 delete_last_character_from_previous_line_in_quoted_block (editor_page
, key_code
, control_key
))
10882 if (!html_mode
&& e_editor_dom_fix_structure_after_delete_before_quoted_content (editor_page
, key_code
, control_key
, delete))
10885 collapsed
= e_editor_dom_selection_is_collapsed (editor_page
);
10887 if (!html_mode
&& !collapsed
&& deleting_block_starting_in_quoted_content (editor_page
, key_code
, control_key
))
10891 /* Let the quote marks be selectable to nearly correctly remove the
10892 * selection. Corrections after are done in body_keyup_event_cb. */
10893 enable_quote_marks_select (document
);
10896 if (key_code
!= ~0)
10897 save_history_for_delete_or_backspace (
10898 editor_page
, key_code
== HTML_KEY_CODE_DELETE
, control_key
);
10900 if (local_delete
) {
10901 WebKitDOMElement
*selection_start_marker
;
10902 WebKitDOMNode
*sibling
, *block
, *next_block
;
10904 /* This needs to be performed just in plain text mode
10905 * and when the selection is collapsed. */
10909 e_editor_dom_selection_save (editor_page
);
10911 selection_start_marker
= webkit_dom_document_get_element_by_id (
10912 document
, "-x-evo-selection-start-marker");
10913 sibling
= webkit_dom_node_get_previous_sibling (
10914 WEBKIT_DOM_NODE (selection_start_marker
));
10915 /* Check if the key was pressed in the beginning of block. */
10916 if (!(sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
10917 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-quoted"))) {
10918 e_editor_dom_selection_restore (editor_page
);
10922 sibling
= webkit_dom_node_get_next_sibling (
10923 WEBKIT_DOM_NODE (selection_start_marker
));
10924 sibling
= webkit_dom_node_get_next_sibling (sibling
);
10926 /* And also the current block was empty. */
10927 if (!(!sibling
|| (sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
10928 !element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-wrap-br")))) {
10929 e_editor_dom_selection_restore (editor_page
);
10933 block
= e_editor_dom_get_parent_block_node_from_child (
10934 WEBKIT_DOM_NODE (selection_start_marker
));
10935 next_block
= webkit_dom_node_get_next_sibling (block
);
10937 remove_node (block
);
10939 e_editor_dom_move_caret_into_element (editor_page
, WEBKIT_DOM_ELEMENT (next_block
), TRUE
);
10943 /* Concatenating a non-quoted block with Backspace key to the
10944 * previous block that is inside a quoted content. */
10945 WebKitDOMElement
*selection_start_marker
;
10946 WebKitDOMNode
*node
, *block
, *prev_block
, *last_child
, *child
;
10948 if (html_mode
|| e_editor_dom_selection_is_citation (editor_page
))
10951 e_editor_dom_selection_save (editor_page
);
10953 selection_start_marker
= webkit_dom_document_get_element_by_id (
10954 document
, "-x-evo-selection-start-marker");
10956 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker
));
10958 e_editor_dom_selection_restore (editor_page
);
10962 remove_empty_blocks (document
);
10964 block
= e_editor_dom_get_parent_block_node_from_child (
10965 WEBKIT_DOM_NODE (selection_start_marker
));
10967 prev_block
= webkit_dom_node_get_previous_sibling (block
);
10968 if (!prev_block
|| !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block
)) {
10969 e_editor_dom_selection_restore (editor_page
);
10973 last_child
= webkit_dom_node_get_last_child (prev_block
);
10974 while (last_child
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child
))
10975 last_child
= webkit_dom_node_get_last_child (last_child
);
10978 e_editor_dom_selection_restore (editor_page
);
10982 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child
));
10983 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child
));
10985 node
= webkit_dom_node_get_last_child (last_child
);
10986 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
))
10987 remove_node (node
);
10989 while ((child
= webkit_dom_node_get_first_child (block
)))
10990 webkit_dom_node_append_child (last_child
, child
, NULL
);
10992 remove_node (block
);
10994 if (WEBKIT_DOM_IS_ELEMENT (last_child
))
10995 e_editor_dom_wrap_and_quote_element (editor_page
, WEBKIT_DOM_ELEMENT (last_child
));
10997 e_editor_dom_selection_restore (editor_page
);
11004 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
11005 e_editor_page_emit_content_changed (editor_page
);
11011 contains_forbidden_elements (WebKitDOMDocument
*document
)
11013 WebKitDOMElement
*body
, *element
;
11015 body
= WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document
));
11017 /* Try to find disallowed elements in the plain text mode */
11018 element
= webkit_dom_element_query_selector (
11021 /* Basic elements used as blocks allowed in the plain text mode */
11022 "[data-evo-paragraph], pre, ul, ol, li, blockquote[type=cite], "
11023 /* Other elements */
11025 /* Indented elements */
11026 ".-x-evo-indented, "
11028 ".-x-evo-signature-wrapper, .-x-evo-signature, "
11030 ".-x-evo-smiley-wrapper, .-x-evo-smiley-img, .-x-evo-smiley-text, "
11031 /* Selection markers */
11032 "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker"
11039 /* Try to find disallowed elements relationship in the plain text */
11040 element
= webkit_dom_element_query_selector (
11043 /* Body descendants */
11044 "body > :matches(blockquote[type=cite], .-x-evo-signature-wrapper), "
11045 /* Main blocks and indented blocks */
11046 ":matches(body, .-x-evo-indented) > :matches(pre, ul, ol, .-x-evo-indented, [data-evo-paragraph]), "
11047 /* Blockquote descendants */
11048 "blockquote[type=cite] > :matches(pre, [data-evo-paragraph], blockquote[type=cite]), "
11049 /* Block descendants */
11050 ":matches(pre, [data-evo-paragraph], li) > :matches(br, span, a), "
11052 ":matches(ul, ol) > :matches(ul, ol, li), "
11054 ".-x-evo-smiley-wrapper > :matches(.-x-evo-smiley-img, .-x-evo-smiley-text), "
11056 ".-x-evo-signature-wrapper > .-x-evo-signature"
11060 return element
? TRUE
: FALSE
;
11064 e_editor_dom_check_if_conversion_needed (EEditorPage
*editor_page
)
11066 WebKitDOMDocument
*document
;
11067 gboolean html_mode
, convert
= FALSE
;
11069 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
11071 document
= e_editor_page_get_document (editor_page
);
11072 html_mode
= e_editor_page_get_html_mode (editor_page
);
11075 convert
= contains_forbidden_elements (document
);
11081 e_editor_dom_process_content_after_mode_change (EEditorPage
*editor_page
)
11083 EEditorUndoRedoManager
*manager
;
11084 gboolean html_mode
;
11086 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11088 html_mode
= e_editor_page_get_html_mode (editor_page
);
11091 process_content_to_html_changing_composer_mode (editor_page
);
11093 process_content_to_plain_text_changing_composer_mode (editor_page
);
11095 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11096 e_editor_undo_redo_manager_clean_history (manager
);
11100 e_editor_dom_get_caret_offset (EEditorPage
*editor_page
)
11102 WebKitDOMDocument
*document
;
11103 WebKitDOMDOMWindow
*dom_window
= NULL
;
11104 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11105 WebKitDOMNode
*anchor
;
11106 WebKitDOMRange
*range
= NULL
;
11110 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
11112 document
= e_editor_page_get_document (editor_page
);
11113 dom_window
= webkit_dom_document_get_default_view (document
);
11114 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11115 g_clear_object (&dom_window
);
11117 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
11118 g_clear_object (&dom_selection
);
11122 webkit_dom_dom_selection_collapse_to_start (dom_selection
, NULL
);
11123 /* Select the text from the current caret position to the beginning of the line. */
11124 webkit_dom_dom_selection_modify (dom_selection
, "extend", "left", "lineBoundary");
11126 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11127 anchor
= webkit_dom_dom_selection_get_anchor_node (dom_selection
);
11128 text
= webkit_dom_range_to_string (range
, NULL
);
11129 ret_val
= strlen (text
);
11132 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
11134 /* In the plain text mode we need to increase the return value by 2 per
11135 * citation level because of "> ". */
11136 if (!e_editor_page_get_html_mode (editor_page
)) {
11137 WebKitDOMNode
*parent
= anchor
;
11139 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
11140 if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent
))
11143 parent
= webkit_dom_node_get_parent_node (parent
);
11147 g_clear_object (&range
);
11148 g_clear_object (&dom_selection
);
11154 e_editor_dom_get_caret_position (EEditorPage
*editor_page
)
11156 WebKitDOMDocument
*document
;
11157 WebKitDOMHTMLElement
*body
;
11158 WebKitDOMDOMWindow
*dom_window
= NULL
;
11159 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11160 WebKitDOMRange
*range
= NULL
, *range_clone
= 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 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11177 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
11179 body
= webkit_dom_document_get_body (document
);
11180 /* Select the text from the beginning of the body to the current caret. */
11181 webkit_dom_range_set_start_before (
11182 range_clone
, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
)), NULL
);
11184 /* This is returning a text without new lines! */
11185 text
= webkit_dom_range_to_string (range_clone
, NULL
);
11186 ret_val
= strlen (text
);
11189 g_clear_object (&range_clone
);
11190 g_clear_object (&range
);
11191 g_clear_object (&dom_selection
);
11197 insert_nbsp_history_event (WebKitDOMDocument
*document
,
11198 EEditorUndoRedoManager
*manager
,
11203 EEditorHistoryEvent
*event
;
11204 WebKitDOMDocumentFragment
*fragment
;
11206 event
= g_new0 (EEditorHistoryEvent
, 1);
11207 event
->type
= HISTORY_AND
;
11208 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11210 fragment
= webkit_dom_document_create_document_fragment (document
);
11211 webkit_dom_node_append_child (
11212 WEBKIT_DOM_NODE (fragment
),
11214 webkit_dom_document_create_text_node (document
, UNICODE_NBSP
)),
11217 event
= g_new0 (EEditorHistoryEvent
, 1);
11218 event
->type
= HISTORY_DELETE
;
11221 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (1));
11223 event
->data
.fragment
= fragment
;
11225 event
->before
.start
.x
= x
;
11226 event
->before
.start
.y
= y
;
11227 event
->before
.end
.x
= x
;
11228 event
->before
.end
.y
= y
;
11230 event
->after
.start
.x
= x
;
11231 event
->after
.start
.y
= y
;
11232 event
->after
.end
.x
= x
;
11233 event
->after
.end
.y
= y
;
11235 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11238 e_editor_dom_save_history_for_drag (EEditorPage
*editor_page
)
11240 WebKitDOMDocument
*document
;
11241 WebKitDOMDocumentFragment
*fragment
;
11242 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11243 WebKitDOMDOMWindow
*dom_window
= NULL
;
11244 WebKitDOMRange
*beginning_of_line
= NULL
;
11245 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
11246 EEditorHistoryEvent
*event
;
11247 EEditorUndoRedoManager
*manager
;
11248 gboolean start_to_start
= FALSE
, end_to_end
= FALSE
;
11252 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11254 document
= e_editor_page_get_document (editor_page
);
11255 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11257 if (!(dom_window
= webkit_dom_document_get_default_view (document
)))
11260 if (!(dom_selection
= webkit_dom_dom_window_get_selection (dom_window
))) {
11261 g_clear_object (&dom_window
);
11265 g_clear_object (&dom_window
);
11267 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
11268 g_clear_object (&dom_selection
);
11272 /* Obtain the dragged content. */
11273 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11274 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
11276 /* Create the history event for the content that will
11277 * be removed by DnD. */
11278 event
= g_new0 (EEditorHistoryEvent
, 1);
11279 event
->type
= HISTORY_DELETE
;
11281 e_editor_dom_selection_get_coordinates (editor_page
,
11282 &event
->before
.start
.x
,
11283 &event
->before
.start
.y
,
11284 &event
->before
.end
.x
,
11285 &event
->before
.end
.y
);
11287 x
= event
->before
.start
.x
;
11288 y
= event
->before
.start
.y
;
11290 event
->after
.start
.x
= x
;
11291 event
->after
.start
.y
= y
;
11292 event
->after
.end
.x
= x
;
11293 event
->after
.end
.y
= y
;
11295 /* Save the content that will be removed. */
11296 fragment
= webkit_dom_range_clone_contents (range_clone
, NULL
);
11298 /* Extend the cloned range to point one character after
11299 * the selection ends to later check if there is a whitespace
11301 webkit_dom_range_set_end (
11303 webkit_dom_range_get_end_container (range_clone
, NULL
),
11304 webkit_dom_range_get_end_offset (range_clone
, NULL
) + 1,
11306 range_text
= webkit_dom_range_get_text (range_clone
);
11308 /* Check if the current selection starts on the beginning of line. */
11309 webkit_dom_dom_selection_modify (
11310 dom_selection
, "extend", "left", "lineboundary");
11311 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11312 start_to_start
= webkit_dom_range_compare_boundary_points (
11313 beginning_of_line
, WEBKIT_DOM_RANGE_START_TO_START
, range
, NULL
) == 0;
11315 /* Restore the selection to state before the check. */
11316 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11317 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11318 g_clear_object (&beginning_of_line
);
11320 /* Check if the current selection end on the end of the line. */
11321 webkit_dom_dom_selection_modify (
11322 dom_selection
, "extend", "right", "lineboundary");
11323 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11324 end_to_end
= webkit_dom_range_compare_boundary_points (
11325 beginning_of_line
, WEBKIT_DOM_RANGE_END_TO_END
, range
, NULL
) == 0;
11327 /* Dragging the whole line. */
11328 if (start_to_start
&& end_to_end
) {
11329 WebKitDOMNode
*container
, *actual_block
, *tmp_block
;
11331 /* Select the whole line (to the beginning of the next
11332 * one so we can reuse the undo code while undoing this.
11333 * Because of this we need to special mark the event
11334 * with history-drag-and-drop to correct the selection
11335 * after undoing it (otherwise the beginning of the next
11336 * line will be selected as well. */
11337 webkit_dom_dom_selection_modify (
11338 dom_selection
, "extend", "right", "character");
11339 g_clear_object (&beginning_of_line
);
11340 beginning_of_line
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11342 container
= webkit_dom_range_get_end_container (range
, NULL
);
11343 actual_block
= e_editor_dom_get_parent_block_node_from_child (container
);
11345 tmp_block
= webkit_dom_range_get_end_container (beginning_of_line
, NULL
);
11346 if ((tmp_block
= e_editor_dom_get_parent_block_node_from_child (tmp_block
))) {
11347 e_editor_dom_selection_get_coordinates (editor_page
,
11348 &event
->before
.start
.x
,
11349 &event
->before
.start
.y
,
11350 &event
->before
.end
.x
,
11351 &event
->before
.end
.y
);
11353 /* Create the right content for the history event. */
11354 fragment
= webkit_dom_document_create_document_fragment (document
);
11355 /* The removed line. */
11356 webkit_dom_node_append_child (
11357 WEBKIT_DOM_NODE (fragment
),
11358 webkit_dom_node_clone_node_with_error (actual_block
, TRUE
, NULL
),
11360 /* The following block, but empty. */
11361 webkit_dom_node_append_child (
11362 WEBKIT_DOM_NODE (fragment
),
11363 webkit_dom_node_clone_node_with_error (tmp_block
, FALSE
, NULL
),
11365 g_object_set_data (
11366 G_OBJECT (fragment
),
11367 "history-drag-and-drop",
11368 GINT_TO_POINTER (1));
11371 /* It should act as a Delete key press. */
11372 g_object_set_data (G_OBJECT (fragment
), "history-delete-key", GINT_TO_POINTER (1));
11374 event
->data
.fragment
= fragment
;
11375 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11377 /* WebKit removes the space (if presented) after selection and
11378 * we need to create a new history event for it. */
11379 if (g_str_has_suffix (range_text
, " ") ||
11380 g_str_has_suffix (range_text
, UNICODE_NBSP
))
11381 insert_nbsp_history_event (document
, manager
, TRUE
, x
, y
);
11383 /* If there is a space before the selection WebKit will remove
11384 * it as well unless there is a space after the selection. */
11385 gchar
*range_text_start
;
11386 glong start_offset
;
11388 start_offset
= webkit_dom_range_get_start_offset (range_clone
, NULL
);
11389 webkit_dom_range_set_start (
11391 webkit_dom_range_get_start_container (range_clone
, NULL
),
11392 start_offset
> 0 ? start_offset
- 1 : 0,
11395 range_text_start
= webkit_dom_range_get_text (range_clone
);
11396 if (g_str_has_prefix (range_text_start
, " ") ||
11397 g_str_has_prefix (range_text_start
, UNICODE_NBSP
)) {
11399 webkit_dom_dom_selection_collapse_to_start (dom_selection
, NULL
);
11400 webkit_dom_dom_selection_modify (
11401 dom_selection
, "move", "backward", "character");
11402 e_editor_dom_selection_get_coordinates (editor_page
, &x
, &y
, &x
, &y
);
11404 insert_nbsp_history_event (document
, manager
, TRUE
, x
, y
);
11407 g_free (range_text_start
);
11410 g_free (range_text
);
11412 /* Restore the selection to original state. */
11413 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11414 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11415 g_clear_object (&beginning_of_line
);
11417 /* All the things above were about removing the content,
11418 * create an AND event to continue later with inserting
11419 * the dropped content. */
11420 event
= g_new0 (EEditorHistoryEvent
, 1);
11421 event
->type
= HISTORY_AND
;
11422 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11424 g_clear_object (&dom_selection
);
11426 g_clear_object (&range
);
11427 g_clear_object (&range_clone
);
11431 e_editor_dom_save_history_for_drop (EEditorPage
*editor_page
)
11433 WebKitDOMDocument
*document
;
11434 WebKitDOMDocumentFragment
*fragment
;
11435 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11436 WebKitDOMDOMWindow
*dom_window
= NULL
;
11437 WebKitDOMNodeList
*list
= NULL
;
11438 WebKitDOMRange
*range
= NULL
;
11439 EEditorUndoRedoManager
*manager
;
11440 EEditorHistoryEvent
*event
;
11443 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11445 document
= e_editor_page_get_document (editor_page
);
11446 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11448 /* When the image is DnD inside the view WebKit removes the wrapper that
11449 * is used for resizing the image, so we have to recreate it again. */
11450 list
= webkit_dom_document_query_selector_all (document
, ":not(span) > img[data-inline]", NULL
);
11451 length
= webkit_dom_node_list_get_length (list
);
11452 for (ii
= 0; ii
< length
; ii
++) {
11453 WebKitDOMElement
*element
;
11454 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
11456 element
= webkit_dom_document_create_element (document
, "span", NULL
);
11457 webkit_dom_element_set_class_name (element
, "-x-evo-resizable-wrapper");
11459 webkit_dom_node_insert_before (
11460 webkit_dom_node_get_parent_node (node
),
11461 WEBKIT_DOM_NODE (element
),
11465 webkit_dom_node_append_child (WEBKIT_DOM_NODE (element
), node
, NULL
);
11467 g_clear_object (&list
);
11469 /* When the image is moved the new selection is created after after it, so
11470 * lets collapse the selection to have the caret right after the image. */
11471 dom_window
= webkit_dom_document_get_default_view (document
);
11472 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11473 g_clear_object (&dom_window
);
11475 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11477 event
= g_new0 (EEditorHistoryEvent
, 1);
11478 event
->type
= HISTORY_INSERT_HTML
;
11480 /* Get the dropped content. It's easy as it is selected by WebKit. */
11481 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
11482 event
->data
.string
.from
= NULL
;
11483 /* Get the HTML content of the dropped content. */
11484 event
->data
.string
.to
= dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment
));
11486 e_editor_undo_redo_manager_insert_history_event (manager
, event
);
11488 g_clear_object (&range
);
11489 g_clear_object (&dom_selection
);
11493 dom_set_link_color_in_document (EEditorPage
*editor_page
,
11494 const gchar
*color
,
11497 WebKitDOMDocument
*document
;
11498 WebKitDOMHTMLHeadElement
*head
;
11499 WebKitDOMElement
*style_element
;
11500 WebKitDOMHTMLElement
*body
;
11501 gchar
*color_str
= NULL
;
11502 const gchar
*style_id
;
11504 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11505 g_return_if_fail (color
!= NULL
);
11507 style_id
= visited
? "-x-evo-a-color-style-visited" : "-x-evo-a-color-style";
11509 document
= e_editor_page_get_document (editor_page
);
11510 head
= webkit_dom_document_get_head (document
);
11511 body
= webkit_dom_document_get_body (document
);
11513 style_element
= webkit_dom_document_get_element_by_id (document
, style_id
);
11514 if (!style_element
) {
11515 style_element
= webkit_dom_document_create_element (document
, "style", NULL
);
11516 webkit_dom_element_set_id (style_element
, style_id
);
11517 webkit_dom_element_set_attribute (style_element
, "type", "text/css", NULL
);
11518 webkit_dom_node_append_child (
11519 WEBKIT_DOM_NODE (head
), WEBKIT_DOM_NODE (style_element
), NULL
);
11522 color_str
= g_strdup_printf (
11523 visited
? "a.-x-evo-visited-link { color: %s; }" : "a { color: %s; }", color
);
11524 webkit_dom_element_set_inner_html (style_element
, color_str
, NULL
);
11525 g_free (color_str
);
11528 webkit_dom_html_body_element_set_v_link (
11529 WEBKIT_DOM_HTML_BODY_ELEMENT (body
), color
);
11531 webkit_dom_html_body_element_set_link (
11532 WEBKIT_DOM_HTML_BODY_ELEMENT (body
), color
);
11536 e_editor_dom_set_link_color (EEditorPage
*editor_page
,
11537 const gchar
*color
)
11539 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11541 dom_set_link_color_in_document (editor_page
, color
, FALSE
);
11545 e_editor_dom_set_visited_link_color (EEditorPage
*editor_page
,
11546 const gchar
*color
)
11548 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11550 dom_set_link_color_in_document (editor_page
, color
, TRUE
);
11554 e_editor_dom_fix_file_uri_images (EEditorPage
*editor_page
)
11556 WebKitDOMDocument
*document
;
11557 WebKitDOMNodeList
*list
= NULL
;
11560 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11562 document
= e_editor_page_get_document (editor_page
);
11564 list
= webkit_dom_document_query_selector_all (
11565 document
, "img[src^=\"file://\"]", NULL
);
11566 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
11567 WebKitDOMNode
*node
;
11570 node
= webkit_dom_node_list_item (list
, ii
);
11571 uri
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "src");
11575 g_clear_object (&list
);
11578 /* ******************** Selection ******************** */
11581 e_editor_dom_replace_base64_image_src (EEditorPage
*editor_page
,
11582 const gchar
*selector
,
11583 const gchar
*base64_content
,
11584 const gchar
*filename
,
11587 WebKitDOMDocument
*document
;
11588 WebKitDOMElement
*element
;
11590 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11592 document
= e_editor_page_get_document (editor_page
);
11593 element
= webkit_dom_document_query_selector (document
, selector
, NULL
);
11595 if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element
))
11596 webkit_dom_html_image_element_set_src (
11597 WEBKIT_DOM_HTML_IMAGE_ELEMENT (element
),
11600 webkit_dom_element_set_attribute (
11601 element
, "background", base64_content
, NULL
);
11603 webkit_dom_element_set_attribute (element
, "data-uri", uri
, NULL
);
11604 webkit_dom_element_set_attribute (element
, "data-inline", "", NULL
);
11605 webkit_dom_element_set_attribute (
11606 element
, "data-name", filename
? filename
: "", NULL
);
11610 e_editor_dom_get_current_range (EEditorPage
*editor_page
)
11612 WebKitDOMDocument
*document
;
11613 WebKitDOMDOMWindow
*dom_window
= NULL
;
11614 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11615 WebKitDOMRange
*range
= NULL
;
11617 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
11619 document
= e_editor_page_get_document (editor_page
);
11620 dom_window
= webkit_dom_document_get_default_view (document
);
11624 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11625 if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection
)) {
11626 g_clear_object (&dom_window
);
11630 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1)
11633 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
11635 g_clear_object (&dom_selection
);
11636 g_clear_object (&dom_window
);
11642 e_editor_dom_move_caret_into_element (EEditorPage
*editor_page
,
11643 WebKitDOMElement
*element
,
11646 WebKitDOMDocument
*document
;
11647 WebKitDOMDOMWindow
*dom_window
= NULL
;
11648 WebKitDOMDOMSelection
*dom_selection
= NULL
;
11649 WebKitDOMRange
*range
= NULL
;
11651 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11656 document
= e_editor_page_get_document (editor_page
);
11657 dom_window
= webkit_dom_document_get_default_view (document
);
11658 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
11659 range
= webkit_dom_document_create_range (document
);
11661 webkit_dom_range_select_node_contents (
11662 range
, WEBKIT_DOM_NODE (element
), NULL
);
11663 webkit_dom_range_collapse (range
, to_start
, NULL
);
11664 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
11665 webkit_dom_dom_selection_add_range (dom_selection
, range
);
11667 g_clear_object (&range
);
11668 g_clear_object (&dom_selection
);
11669 g_clear_object (&dom_window
);
11673 e_editor_dom_insert_base64_image (EEditorPage
*editor_page
,
11674 const gchar
*base64_content
,
11675 const gchar
*filename
,
11678 WebKitDOMDocument
*document
;
11679 WebKitDOMElement
*element
, *selection_start_marker
, *resizable_wrapper
;
11680 WebKitDOMText
*text
;
11681 EEditorHistoryEvent
*ev
= NULL
;
11682 EEditorUndoRedoManager
*manager
;
11684 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
11686 document
= e_editor_page_get_document (editor_page
);
11687 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
11689 if (!e_editor_dom_selection_is_collapsed (editor_page
)) {
11690 EEditorHistoryEvent
*ev
;
11691 WebKitDOMDocumentFragment
*fragment
;
11692 WebKitDOMRange
*range
= NULL
;
11694 ev
= g_new0 (EEditorHistoryEvent
, 1);
11695 ev
->type
= HISTORY_DELETE
;
11697 range
= e_editor_dom_get_current_range (editor_page
);
11698 fragment
= webkit_dom_range_clone_contents (range
, NULL
);
11699 g_clear_object (&range
);
11700 ev
->data
.fragment
= g_object_ref (fragment
);
11702 e_editor_dom_selection_get_coordinates (editor_page
,
11703 &ev
->before
.start
.x
,
11704 &ev
->before
.start
.y
,
11706 &ev
->before
.end
.y
);
11708 ev
->after
.start
.x
= ev
->before
.start
.x
;
11709 ev
->after
.start
.y
= ev
->before
.start
.y
;
11710 ev
->after
.end
.x
= ev
->before
.start
.x
;
11711 ev
->after
.end
.y
= ev
->before
.start
.y
;
11713 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11715 ev
= g_new0 (EEditorHistoryEvent
, 1);
11716 ev
->type
= HISTORY_AND
;
11718 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11719 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_DELETE
, NULL
);
11722 e_editor_dom_selection_save (editor_page
);
11723 selection_start_marker
= webkit_dom_document_get_element_by_id (
11724 document
, "-x-evo-selection-start-marker");
11726 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
11727 ev
= g_new0 (EEditorHistoryEvent
, 1);
11728 ev
->type
= HISTORY_IMAGE
;
11730 e_editor_dom_selection_get_coordinates (editor_page
,
11731 &ev
->before
.start
.x
,
11732 &ev
->before
.start
.y
,
11734 &ev
->before
.end
.y
);
11737 resizable_wrapper
=
11738 webkit_dom_document_create_element (document
, "span", NULL
);
11739 webkit_dom_element_set_attribute (
11740 resizable_wrapper
, "class", "-x-evo-resizable-wrapper", NULL
);
11742 element
= webkit_dom_document_create_element (document
, "img", NULL
);
11743 webkit_dom_html_image_element_set_src (
11744 WEBKIT_DOM_HTML_IMAGE_ELEMENT (element
),
11746 webkit_dom_element_set_attribute (
11747 WEBKIT_DOM_ELEMENT (element
), "data-uri", uri
, NULL
);
11748 webkit_dom_element_set_attribute (
11749 WEBKIT_DOM_ELEMENT (element
), "data-inline", "", NULL
);
11750 webkit_dom_element_set_attribute (
11751 WEBKIT_DOM_ELEMENT (element
), "data-name",
11752 filename
? filename
: "", NULL
);
11753 webkit_dom_node_append_child (
11754 WEBKIT_DOM_NODE (resizable_wrapper
),
11755 WEBKIT_DOM_NODE (element
),
11758 webkit_dom_node_insert_before (
11759 webkit_dom_node_get_parent_node (
11760 WEBKIT_DOM_NODE (selection_start_marker
)),
11761 WEBKIT_DOM_NODE (resizable_wrapper
),
11762 WEBKIT_DOM_NODE (selection_start_marker
),
11765 /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
11766 * caret on right position */
11767 text
= webkit_dom_document_create_text_node (
11768 document
, UNICODE_ZERO_WIDTH_SPACE
);
11770 webkit_dom_node_insert_before (
11771 webkit_dom_node_get_parent_node (
11772 WEBKIT_DOM_NODE (selection_start_marker
)),
11773 WEBKIT_DOM_NODE (text
),
11774 WEBKIT_DOM_NODE (selection_start_marker
),
11778 WebKitDOMDocumentFragment
*fragment
;
11779 WebKitDOMNode
*node
;
11781 fragment
= webkit_dom_document_create_document_fragment (document
);
11782 node
= webkit_dom_node_append_child (
11783 WEBKIT_DOM_NODE (fragment
),
11784 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (resizable_wrapper
), TRUE
, NULL
),
11787 webkit_dom_element_insert_adjacent_html (
11788 WEBKIT_DOM_ELEMENT (node
), "afterend", "​", NULL
);
11789 ev
->data
.fragment
= g_object_ref (fragment
);
11791 e_editor_dom_selection_get_coordinates (editor_page
,
11792 &ev
->after
.start
.x
,
11793 &ev
->after
.start
.y
,
11797 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
11800 e_editor_dom_selection_restore (editor_page
);
11801 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
11802 e_editor_dom_scroll_to_caret (editor_page
);
11805 /* ************************ image_load_and_insert_async() ************************ */
11807 typedef struct _ImageLoadContext
{
11808 EEditorPage
*editor_page
;
11809 GInputStream
*input_stream
;
11810 GOutputStream
*output_stream
;
11812 GFileInfo
*file_info
;
11813 goffset total_num_bytes
;
11815 const gchar
*content_type
;
11816 const gchar
*filename
;
11817 const gchar
*selector
;
11818 gchar buffer
[4096];
11819 } ImageLoadContext
;
11821 /* Forward Declaration */
11823 image_load_stream_read_cb (GInputStream
*input_stream
,
11824 GAsyncResult
*result
,
11825 ImageLoadContext
*load_context
);
11827 static ImageLoadContext
*
11828 image_load_context_new (EEditorPage
*editor_page
)
11830 ImageLoadContext
*load_context
;
11832 load_context
= g_slice_new0 (ImageLoadContext
);
11833 load_context
->editor_page
= editor_page
;
11835 return load_context
;
11839 image_load_context_free (ImageLoadContext
*load_context
)
11841 if (load_context
->input_stream
!= NULL
)
11842 g_object_unref (load_context
->input_stream
);
11844 if (load_context
->output_stream
!= NULL
)
11845 g_object_unref (load_context
->output_stream
);
11847 if (load_context
->file_info
!= NULL
)
11848 g_object_unref (load_context
->file_info
);
11850 if (load_context
->file
!= NULL
)
11851 g_object_unref (load_context
->file
);
11853 g_slice_free (ImageLoadContext
, load_context
);
11857 image_load_finish (ImageLoadContext
*load_context
)
11859 EEditorPage
*editor_page
;
11860 GMemoryOutputStream
*output_stream
;
11861 const gchar
*selector
;
11862 gchar
*base64_encoded
, *mime_type
, *output
, *uri
;
11866 output_stream
= G_MEMORY_OUTPUT_STREAM (load_context
->output_stream
);
11867 editor_page
= load_context
->editor_page
;
11868 mime_type
= g_content_type_get_mime_type (load_context
->content_type
);
11870 data
= g_memory_output_stream_get_data (output_stream
);
11871 size
= g_memory_output_stream_get_data_size (output_stream
);
11872 uri
= g_file_get_uri (load_context
->file
);
11874 base64_encoded
= g_base64_encode ((const guchar
*) data
, size
);
11875 output
= g_strconcat ("data:", mime_type
, ";base64,", base64_encoded
, NULL
);
11876 selector
= load_context
->selector
;
11877 if (selector
&& *selector
)
11878 e_editor_dom_replace_base64_image_src (editor_page
, selector
, output
, load_context
->filename
, uri
);
11880 e_editor_dom_insert_base64_image (editor_page
, output
, load_context
->filename
, uri
);
11882 g_free (base64_encoded
);
11884 g_free (mime_type
);
11887 image_load_context_free (load_context
);
11891 image_load_write_cb (GOutputStream
*output_stream
,
11892 GAsyncResult
*result
,
11893 ImageLoadContext
*load_context
)
11895 GInputStream
*input_stream
;
11896 gssize bytes_written
;
11897 GError
*error
= NULL
;
11899 bytes_written
= g_output_stream_write_finish (
11900 output_stream
, result
, &error
);
11903 image_load_context_free (load_context
);
11907 input_stream
= load_context
->input_stream
;
11909 if (bytes_written
< load_context
->bytes_read
) {
11911 load_context
->buffer
,
11912 load_context
->buffer
+ bytes_written
,
11913 load_context
->bytes_read
- bytes_written
);
11914 load_context
->bytes_read
-= bytes_written
;
11916 g_output_stream_write_async (
11918 load_context
->buffer
,
11919 load_context
->bytes_read
,
11920 G_PRIORITY_DEFAULT
, NULL
,
11921 (GAsyncReadyCallback
) image_load_write_cb
,
11924 g_input_stream_read_async (
11926 load_context
->buffer
,
11927 sizeof (load_context
->buffer
),
11928 G_PRIORITY_DEFAULT
, NULL
,
11929 (GAsyncReadyCallback
) image_load_stream_read_cb
,
11934 image_load_stream_read_cb (GInputStream
*input_stream
,
11935 GAsyncResult
*result
,
11936 ImageLoadContext
*load_context
)
11938 GOutputStream
*output_stream
;
11940 GError
*error
= NULL
;
11942 bytes_read
= g_input_stream_read_finish (
11943 input_stream
, result
, &error
);
11946 image_load_context_free (load_context
);
11950 if (bytes_read
== 0) {
11951 image_load_finish (load_context
);
11955 output_stream
= load_context
->output_stream
;
11956 load_context
->bytes_read
= bytes_read
;
11958 g_output_stream_write_async (
11960 load_context
->buffer
,
11961 load_context
->bytes_read
,
11962 G_PRIORITY_DEFAULT
, NULL
,
11963 (GAsyncReadyCallback
) image_load_write_cb
,
11968 image_load_file_read_cb (GFile
*file
,
11969 GAsyncResult
*result
,
11970 ImageLoadContext
*load_context
)
11972 GFileInputStream
*input_stream
;
11973 GOutputStream
*output_stream
;
11974 GError
*error
= NULL
;
11976 /* Input stream might be NULL, so don't use cast macro. */
11977 input_stream
= g_file_read_finish (file
, result
, &error
);
11978 load_context
->input_stream
= (GInputStream
*) input_stream
;
11981 image_load_context_free (load_context
);
11985 /* Load the contents into a GMemoryOutputStream. */
11986 output_stream
= g_memory_output_stream_new (
11987 NULL
, 0, g_realloc
, g_free
);
11989 load_context
->output_stream
= output_stream
;
11991 g_input_stream_read_async (
11992 load_context
->input_stream
,
11993 load_context
->buffer
,
11994 sizeof (load_context
->buffer
),
11995 G_PRIORITY_DEFAULT
, NULL
,
11996 (GAsyncReadyCallback
) image_load_stream_read_cb
,
12001 image_load_query_info_cb (GFile
*file
,
12002 GAsyncResult
*result
,
12003 ImageLoadContext
*load_context
)
12005 GFileInfo
*file_info
;
12006 GError
*error
= NULL
;
12008 file_info
= g_file_query_info_finish (file
, result
, &error
);
12010 image_load_context_free (load_context
);
12014 load_context
->content_type
= g_file_info_get_content_type (file_info
);
12015 load_context
->total_num_bytes
= g_file_info_get_size (file_info
);
12016 load_context
->filename
= g_file_info_get_name (file_info
);
12018 g_file_read_async (
12019 file
, G_PRIORITY_DEFAULT
,
12020 NULL
, (GAsyncReadyCallback
)
12021 image_load_file_read_cb
, load_context
);
12025 image_load_and_insert_async (EEditorPage
*editor_page
,
12026 const gchar
*selector
,
12029 ImageLoadContext
*load_context
;
12032 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12033 g_return_if_fail (uri
&& *uri
);
12035 file
= g_file_new_for_uri (uri
);
12036 g_return_if_fail (file
!= NULL
);
12038 load_context
= image_load_context_new (editor_page
);
12039 load_context
->file
= file
;
12040 if (selector
&& *selector
)
12041 load_context
->selector
= g_strdup (selector
);
12043 g_file_query_info_async (
12044 file
, "standard::*",
12045 G_FILE_QUERY_INFO_NONE
,G_PRIORITY_DEFAULT
,
12046 NULL
, (GAsyncReadyCallback
)
12047 image_load_query_info_cb
, load_context
);
12051 e_editor_dom_insert_image (EEditorPage
*editor_page
,
12054 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12056 if (!e_editor_page_get_html_mode (editor_page
))
12059 if (strstr (uri
, ";base64,")) {
12060 if (g_str_has_prefix (uri
, "data:"))
12061 e_editor_dom_insert_base64_image (editor_page
, uri
, "", "");
12062 if (strstr (uri
, ";data")) {
12063 const gchar
*base64_data
= strstr (uri
, ";") + 1;
12065 glong filename_length
;
12068 g_utf8_strlen (uri
, -1) -
12069 g_utf8_strlen (base64_data
, -1) - 1;
12070 filename
= g_strndup (uri
, filename_length
);
12072 e_editor_dom_insert_base64_image (editor_page
, base64_data
, filename
, "");
12076 image_load_and_insert_async (editor_page
, NULL
, uri
);
12080 e_editor_dom_replace_image_src (EEditorPage
*editor_page
,
12081 const gchar
*selector
,
12084 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12086 if (strstr (uri
, ";base64,")) {
12087 if (g_str_has_prefix (uri
, "data:"))
12088 e_editor_dom_replace_base64_image_src (
12089 editor_page
, selector
, uri
, "", "");
12090 if (strstr (uri
, ";data")) {
12091 const gchar
*base64_data
= strstr (uri
, ";") + 1;
12093 glong filename_length
;
12096 g_utf8_strlen (uri
, -1) -
12097 g_utf8_strlen (base64_data
, -1) - 1;
12098 filename
= g_strndup (uri
, filename_length
);
12100 e_editor_dom_replace_base64_image_src (
12101 editor_page
, selector
, base64_data
, filename
, "");
12105 image_load_and_insert_async (editor_page
, selector
, uri
);
12109 * e_html_editor_selection_unlink:
12110 * @selection: an #EEditorSelection
12112 * Removes any links (<A> elements) from current selection or at current
12116 e_editor_dom_selection_unlink (EEditorPage
*editor_page
)
12118 WebKitDOMDocument
*document
;
12119 WebKitDOMDOMWindow
*dom_window
= NULL
;
12120 WebKitDOMDOMSelection
*dom_selection
= NULL
;
12121 WebKitDOMRange
*range
= NULL
;
12122 WebKitDOMElement
*link
;
12123 EEditorUndoRedoManager
*manager
;
12126 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12128 document
= e_editor_page_get_document (editor_page
);
12129 dom_window
= webkit_dom_document_get_default_view (document
);
12130 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
12132 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
12133 link
= dom_node_find_parent_element (
12134 webkit_dom_range_get_start_container (range
, NULL
), "A");
12136 g_clear_object (&dom_selection
);
12137 g_clear_object (&dom_window
);
12140 WebKitDOMNode
*node
;
12142 /* get element that was clicked on */
12143 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
12144 if (node
&& !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
)) {
12145 link
= dom_node_find_parent_element (node
, "A");
12146 if (link
&& !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link
)) {
12147 g_clear_object (&range
);
12150 link
= WEBKIT_DOM_ELEMENT (node
);
12154 g_clear_object (&range
);
12159 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
12160 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
12161 EEditorHistoryEvent
*ev
;
12162 WebKitDOMDocumentFragment
*fragment
;
12164 ev
= g_new0 (EEditorHistoryEvent
, 1);
12165 ev
->type
= HISTORY_REMOVE_LINK
;
12167 e_editor_dom_selection_get_coordinates (editor_page
,
12168 &ev
->before
.start
.x
,
12169 &ev
->before
.start
.y
,
12171 &ev
->before
.end
.y
);
12173 fragment
= webkit_dom_document_create_document_fragment (document
);
12174 webkit_dom_node_append_child (
12175 WEBKIT_DOM_NODE (fragment
),
12176 webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (link
), TRUE
, NULL
),
12178 ev
->data
.fragment
= g_object_ref (fragment
);
12180 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
12183 text
= webkit_dom_html_element_get_inner_text (
12184 WEBKIT_DOM_HTML_ELEMENT (link
));
12185 webkit_dom_element_set_outer_html (link
, text
, NULL
);
12190 * e_html_editor_selection_create_link:
12191 * @document: a @WebKitDOMDocument
12192 * @uri: destination of the new link
12194 * Converts current selection into a link pointing to @url.
12197 e_editor_dom_create_link (EEditorPage
*editor_page
,
12200 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12201 g_return_if_fail (uri
!= NULL
&& *uri
!= '\0');
12203 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_CREATE_LINK
, uri
);
12207 get_list_level (WebKitDOMNode
*node
)
12211 while (node
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
)) {
12212 if (node_is_list (node
))
12214 node
= webkit_dom_node_get_parent_node (node
);
12221 set_ordered_list_type_to_element (WebKitDOMElement
*list
,
12222 EContentEditorBlockFormat format
)
12224 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
)
12225 webkit_dom_element_remove_attribute (list
, "type");
12226 else if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
)
12227 webkit_dom_element_set_attribute (list
, "type", "A", NULL
);
12228 else if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN
)
12229 webkit_dom_element_set_attribute (list
, "type", "I", NULL
);
12232 static const gchar
*
12233 get_css_alignment_value_class (EContentEditorAlignment alignment
)
12235 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_LEFT
)
12236 return ""; /* Left is by default on ltr */
12238 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_CENTER
)
12239 return "-x-evo-align-center";
12241 if (alignment
== E_CONTENT_EDITOR_ALIGNMENT_RIGHT
)
12242 return "-x-evo-align-right";
12248 * e_html_editor_selection_get_alignment:
12249 * @selection: #an EEditorSelection
12251 * Returns alignment of current paragraph
12253 * Returns: #EContentEditorAlignment
12255 static EContentEditorAlignment
12256 dom_get_alignment (EEditorPage
*editor_page
)
12258 WebKitDOMDocument
*document
;
12259 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
12260 WebKitDOMDOMWindow
*dom_window
= NULL
;
12261 WebKitDOMElement
*element
;
12262 WebKitDOMNode
*node
;
12263 WebKitDOMRange
*range
= NULL
;
12264 EContentEditorAlignment alignment
;
12267 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_ALIGNMENT_LEFT
);
12269 document
= e_editor_page_get_document (editor_page
);
12270 range
= e_editor_dom_get_current_range (editor_page
);
12272 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12274 node
= webkit_dom_range_get_start_container (range
, NULL
);
12275 g_clear_object (&range
);
12277 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12279 if (WEBKIT_DOM_IS_ELEMENT (node
))
12280 element
= WEBKIT_DOM_ELEMENT (node
);
12282 element
= WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (node
));
12284 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (element
)) {
12285 if (element_has_class (element
, "-x-evo-align-right"))
12286 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12287 else if (element_has_class (element
, "-x-evo-align-center"))
12288 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12290 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12295 dom_window
= webkit_dom_document_get_default_view (document
);
12296 style
= webkit_dom_dom_window_get_computed_style (dom_window
, element
, NULL
);
12297 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
12299 if (!value
|| !*value
||
12300 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
12301 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12302 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
12303 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12304 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
12305 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12307 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12310 g_clear_object (&dom_window
);
12311 g_clear_object (&style
);
12318 set_word_wrap_length (EEditorPage
*editor_page
,
12319 gint user_word_wrap_length
)
12321 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
12323 /* user_word_wrap_length < 0, set block width to word_wrap_length
12324 * user_word_wrap_length == 0, no width limit set,
12325 * user_word_wrap_length > 0, set width limit to given value */
12326 return (user_word_wrap_length
< 0) ?
12327 e_editor_page_get_word_wrap_length (editor_page
) : user_word_wrap_length
;
12331 e_editor_dom_set_paragraph_style (EEditorPage
*editor_page
,
12332 WebKitDOMElement
*element
,
12335 const gchar
*style_to_add
)
12337 WebKitDOMNode
*parent
;
12338 gchar
*style
= NULL
;
12339 gint word_wrap_length
;
12341 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12343 word_wrap_length
= set_word_wrap_length (editor_page
, width
);
12344 webkit_dom_element_set_attribute (element
, "data-evo-paragraph", "", NULL
);
12346 /* Don't set the alignment for nodes as they are handled separately. */
12347 if (!node_is_list (WEBKIT_DOM_NODE (element
))) {
12348 EContentEditorAlignment alignment
;
12350 alignment
= dom_get_alignment (editor_page
);
12351 element_add_class (element
, get_css_alignment_value_class (alignment
));
12354 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
12355 /* Don't set the width limit to sub-blocks as the width limit is inhered
12356 * from its parents. */
12357 if (!e_editor_page_get_html_mode (editor_page
) &&
12358 (!parent
|| WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
))) {
12359 style
= g_strdup_printf (
12360 "width: %dch;%s%s",
12361 (word_wrap_length
+ offset
),
12362 style_to_add
&& *style_to_add
? " " : "",
12363 style_to_add
&& *style_to_add
? style_to_add
: "");
12365 if (style_to_add
&& *style_to_add
)
12366 style
= g_strdup_printf ("%s", style_to_add
);
12369 webkit_dom_element_set_attribute (element
, "style", style
, NULL
);
12374 static WebKitDOMElement
*
12375 create_list_element (EEditorPage
*editor_page
,
12376 EContentEditorBlockFormat format
,
12378 gboolean html_mode
)
12380 WebKitDOMDocument
*document
;
12381 WebKitDOMElement
*list
;
12382 gboolean inserting_unordered_list
;
12384 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12386 document
= e_editor_page_get_document (editor_page
);
12387 inserting_unordered_list
= format
== E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
12389 list
= webkit_dom_document_create_element (
12390 document
, inserting_unordered_list
? "UL" : "OL", NULL
);
12392 if (!inserting_unordered_list
)
12393 set_ordered_list_type_to_element (list
, format
);
12395 if (level
>= 0 && !html_mode
) {
12398 offset
= (level
+ 1) * SPACES_PER_LIST_LEVEL
;
12400 offset
+= !inserting_unordered_list
?
12401 SPACES_ORDERED_LIST_FIRST_LEVEL
- SPACES_PER_LIST_LEVEL
: 0;
12403 e_editor_dom_set_paragraph_style (editor_page
, list
, -1, -offset
, NULL
);
12410 indent_list (EEditorPage
*editor_page
)
12412 WebKitDOMDocument
*document
;
12413 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
12414 WebKitDOMNode
*item
, *next_item
;
12415 gboolean after_selection_end
= FALSE
;
12417 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
12419 document
= e_editor_page_get_document (editor_page
);
12420 selection_start_marker
= webkit_dom_document_get_element_by_id (
12421 document
, "-x-evo-selection-start-marker");
12422 selection_end_marker
= webkit_dom_document_get_element_by_id (
12423 document
, "-x-evo-selection-end-marker");
12425 item
= e_editor_dom_get_parent_block_node_from_child (
12426 WEBKIT_DOM_NODE (selection_start_marker
));
12428 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
12429 gboolean html_mode
= e_editor_page_get_html_mode (editor_page
);
12430 WebKitDOMElement
*list
;
12431 WebKitDOMNode
*source_list
= webkit_dom_node_get_parent_node (item
);
12432 EContentEditorBlockFormat format
;
12434 format
= dom_get_list_format_from_node (source_list
);
12436 list
= create_list_element (
12437 editor_page
, format
, get_list_level (item
), html_mode
);
12439 element_add_class (list
, "-x-evo-indented");
12441 webkit_dom_node_insert_before (
12442 source_list
, WEBKIT_DOM_NODE (list
), item
, NULL
);
12444 while (item
&& !after_selection_end
) {
12445 after_selection_end
= webkit_dom_node_contains (
12446 item
, WEBKIT_DOM_NODE (selection_end_marker
));
12448 next_item
= webkit_dom_node_get_next_sibling (item
);
12450 webkit_dom_node_append_child (
12451 WEBKIT_DOM_NODE (list
), item
, NULL
);
12456 merge_lists_if_possible (WEBKIT_DOM_NODE (list
));
12459 return after_selection_end
;
12463 dom_set_indented_style (EEditorPage
*editor_page
,
12464 WebKitDOMElement
*element
,
12468 gint word_wrap_length
;
12470 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12472 word_wrap_length
= set_word_wrap_length (editor_page
, width
);
12473 webkit_dom_element_set_class_name (element
, "-x-evo-indented");
12475 if (e_editor_page_get_html_mode (editor_page
) || word_wrap_length
== 0) {
12476 style
= g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION
);
12478 if (word_wrap_length
!= 0) {
12479 gchar
*plain_text_style
;
12481 plain_text_style
= g_strdup_printf (
12482 "margin-left: %dch; word-wrap: normal; width: %dch;",
12483 SPACES_PER_INDENTATION
, word_wrap_length
);
12485 webkit_dom_element_set_attribute (
12486 element
, "data-plain-text-style", plain_text_style
, NULL
);
12487 g_free (plain_text_style
);
12490 style
= g_strdup_printf (
12491 "margin-left: %dch; word-wrap: normal; width: %dch;",
12492 SPACES_PER_INDENTATION
, word_wrap_length
);
12495 webkit_dom_element_set_attribute (element
, "style", style
, NULL
);
12499 static WebKitDOMElement
*
12500 dom_get_indented_element (EEditorPage
*editor_page
,
12503 WebKitDOMDocument
*document
;
12504 WebKitDOMElement
*element
;
12506 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12508 document
= e_editor_page_get_document (editor_page
);
12509 element
= webkit_dom_document_create_element (document
, "DIV", NULL
);
12510 dom_set_indented_style (editor_page
, element
, width
);
12515 static WebKitDOMNode
*
12516 indent_block (EEditorPage
*editor_page
,
12517 WebKitDOMNode
*block
,
12520 WebKitDOMElement
*element
;
12521 WebKitDOMNode
*sibling
, *tmp
;
12523 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
12525 sibling
= webkit_dom_node_get_previous_sibling (block
);
12526 if (WEBKIT_DOM_IS_ELEMENT (sibling
) &&
12527 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-indented")) {
12528 element
= WEBKIT_DOM_ELEMENT (sibling
);
12530 element
= dom_get_indented_element (editor_page
, width
);
12532 webkit_dom_node_insert_before (
12533 webkit_dom_node_get_parent_node (block
),
12534 WEBKIT_DOM_NODE (element
),
12539 /* Remove style and let the paragraph inherit it from parent */
12540 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph"))
12541 webkit_dom_element_remove_attribute (
12542 WEBKIT_DOM_ELEMENT (block
), "style");
12544 tmp
= webkit_dom_node_append_child (
12545 WEBKIT_DOM_NODE (element
),
12549 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
12551 while (WEBKIT_DOM_IS_ELEMENT (sibling
) &&
12552 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "-x-evo-indented")) {
12553 WebKitDOMNode
*next_sibling
;
12554 WebKitDOMNode
*child
;
12556 next_sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (sibling
));
12558 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (sibling
)))) {
12559 webkit_dom_node_append_child (
12560 WEBKIT_DOM_NODE (element
),
12564 remove_node (sibling
);
12565 sibling
= next_sibling
;
12571 static WebKitDOMNode
*
12572 get_list_item_node_from_child (WebKitDOMNode
*child
)
12574 WebKitDOMNode
*parent
= webkit_dom_node_get_parent_node (child
);
12576 while (parent
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent
))
12577 parent
= webkit_dom_node_get_parent_node (parent
);
12582 static WebKitDOMNode
*
12583 get_list_node_from_child (WebKitDOMNode
*child
)
12585 WebKitDOMNode
*parent
= get_list_item_node_from_child (child
);
12587 return webkit_dom_node_get_parent_node (parent
);
12591 do_format_change_list_to_block (EEditorPage
*editor_page
,
12592 EContentEditorBlockFormat format
,
12593 WebKitDOMNode
*item
,
12594 const gchar
*value
)
12596 WebKitDOMDocument
*document
;
12597 WebKitDOMElement
*element
, *selection_end
;
12598 WebKitDOMNode
*node
, *source_list
;
12599 gboolean after_end
= FALSE
;
12602 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
12604 document
= e_editor_page_get_document (editor_page
);
12605 selection_end
= webkit_dom_document_get_element_by_id (
12606 document
, "-x-evo-selection-end-marker");
12608 source_list
= webkit_dom_node_get_parent_node (item
);
12609 while (source_list
) {
12610 WebKitDOMNode
*parent
;
12612 parent
= webkit_dom_node_get_parent_node (source_list
);
12613 if (node_is_list (parent
))
12614 source_list
= parent
;
12619 if (webkit_dom_node_contains (source_list
, WEBKIT_DOM_NODE (selection_end
)))
12620 source_list
= split_list_into_two (item
, -1);
12622 source_list
= webkit_dom_node_get_next_sibling (source_list
);
12625 /* Process all nodes that are in selection one by one */
12626 while (item
&& WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
12627 WebKitDOMNode
*next_item
;
12629 next_item
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (item
));
12631 WebKitDOMNode
*parent
;
12632 WebKitDOMNode
*tmp
= item
;
12635 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp
));
12636 if (!node_is_list (parent
))
12639 next_item
= webkit_dom_node_get_next_sibling (parent
);
12640 if (node_is_list (next_item
)) {
12641 next_item
= webkit_dom_node_get_first_child (next_item
);
12643 } else if (next_item
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12644 next_item
= webkit_dom_node_get_next_sibling (next_item
);
12646 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12651 } else if (node_is_list (next_item
)) {
12652 next_item
= webkit_dom_node_get_first_child (next_item
);
12653 } else if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item
)) {
12654 next_item
= webkit_dom_node_get_next_sibling (item
);
12659 after_end
= webkit_dom_node_contains (item
, WEBKIT_DOM_NODE (selection_end
));
12661 level
= get_indentation_level (WEBKIT_DOM_ELEMENT (item
));
12663 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
) {
12664 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
12666 element
= webkit_dom_document_create_element (
12667 document
, value
, NULL
);
12669 while ((node
= webkit_dom_node_get_first_child (item
)))
12670 webkit_dom_node_append_child (
12671 WEBKIT_DOM_NODE (element
), node
, NULL
);
12673 webkit_dom_node_insert_before (
12674 webkit_dom_node_get_parent_node (source_list
),
12675 WEBKIT_DOM_NODE (element
),
12680 gint final_width
= 0;
12682 node
= WEBKIT_DOM_NODE (element
);
12684 if (webkit_dom_element_has_attribute (element
, "data-evo-paragraph"))
12685 final_width
= e_editor_page_get_word_wrap_length (editor_page
) -
12686 SPACES_PER_INDENTATION
* level
;
12689 node
= indent_block (editor_page
, node
, final_width
);
12692 e_editor_dom_remove_node_and_parents_if_empty (item
);
12699 remove_node_if_empty (source_list
);
12705 format_change_list_to_block (EEditorPage
*editor_page
,
12706 EContentEditorBlockFormat format
,
12707 const gchar
*value
)
12709 WebKitDOMDocument
*document
;
12710 WebKitDOMElement
*selection_start
;
12711 WebKitDOMNode
*item
;
12713 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12715 document
= e_editor_page_get_document (editor_page
);
12717 selection_start
= webkit_dom_document_get_element_by_id (
12718 document
, "-x-evo-selection-start-marker");
12720 item
= get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start
));
12722 do_format_change_list_to_block (editor_page
, format
, item
, value
);
12725 static WebKitDOMNode
*
12726 get_parent_indented_block (WebKitDOMNode
*node
)
12728 WebKitDOMNode
*parent
, *block
= NULL
;
12730 parent
= webkit_dom_node_get_parent_node (node
);
12731 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-indented"))
12734 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
12735 if (element_has_class (WEBKIT_DOM_ELEMENT (parent
), "-x-evo-indented"))
12737 parent
= webkit_dom_node_get_parent_node (parent
);
12743 static WebKitDOMElement
*
12744 get_element_for_inspection (WebKitDOMRange
*range
)
12746 WebKitDOMNode
*node
;
12748 node
= webkit_dom_range_get_end_container (range
, NULL
);
12749 /* No selection or whole body selected */
12750 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
12753 return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
12756 static EContentEditorAlignment
12757 dom_get_alignment_from_node (WebKitDOMNode
*node
)
12759 EContentEditorAlignment alignment
;
12761 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
12763 style
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node
));
12764 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
12766 if (!value
|| !*value
||
12767 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
12768 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12769 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
12770 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
12771 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
12772 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
12774 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
12777 g_clear_object (&style
);
12784 * e_html_editor_selection_indent:
12785 * @selection: an #EEditorSelection
12787 * Indents current paragraph by one level.
12790 e_editor_dom_selection_indent (EEditorPage
*editor_page
)
12792 WebKitDOMDocument
*document
;
12793 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
12794 WebKitDOMNode
*block
;
12795 EEditorHistoryEvent
*ev
= NULL
;
12796 EEditorUndoRedoManager
*manager
;
12797 gboolean after_selection_start
= FALSE
, after_selection_end
= FALSE
;
12799 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
12801 document
= e_editor_page_get_document (editor_page
);
12802 e_editor_dom_selection_save (editor_page
);
12804 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
12806 selection_start_marker
= webkit_dom_document_get_element_by_id (
12807 document
, "-x-evo-selection-start-marker");
12808 selection_end_marker
= webkit_dom_document_get_element_by_id (
12809 document
, "-x-evo-selection-end-marker");
12811 /* If the selection was not saved, move it into the first child of body */
12812 if (!selection_start_marker
|| !selection_end_marker
) {
12813 WebKitDOMHTMLElement
*body
;
12814 WebKitDOMNode
*child
;
12816 body
= webkit_dom_document_get_body (document
);
12817 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
12819 dom_add_selection_markers_into_element_start (
12821 WEBKIT_DOM_ELEMENT (child
),
12822 &selection_start_marker
,
12823 &selection_end_marker
);
12826 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
12827 ev
= g_new0 (EEditorHistoryEvent
, 1);
12828 ev
->type
= HISTORY_INDENT
;
12830 e_editor_dom_selection_get_coordinates (editor_page
,
12831 &ev
->before
.start
.x
,
12832 &ev
->before
.start
.y
,
12834 &ev
->before
.end
.y
);
12836 ev
->data
.style
.from
= 1;
12837 ev
->data
.style
.to
= 1;
12840 block
= get_parent_indented_block (
12841 WEBKIT_DOM_NODE (selection_start_marker
));
12843 block
= e_editor_dom_get_parent_block_node_from_child (
12844 WEBKIT_DOM_NODE (selection_start_marker
));
12846 while (block
&& !after_selection_end
) {
12847 gint ii
, length
, level
, word_wrap_length
, final_width
= 0;
12848 WebKitDOMNode
*next_block
;
12849 WebKitDOMNodeList
*list
= NULL
;
12851 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
12853 next_block
= webkit_dom_node_get_next_sibling (block
);
12855 list
= webkit_dom_element_query_selector_all (
12856 WEBKIT_DOM_ELEMENT (block
),
12857 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
12860 after_selection_end
= webkit_dom_node_contains (
12861 block
, WEBKIT_DOM_NODE (selection_end_marker
));
12863 length
= webkit_dom_node_list_get_length (list
);
12864 if (length
== 0 && node_is_list_or_item (block
)) {
12865 after_selection_end
= indent_list (editor_page
);
12870 if (!after_selection_start
) {
12871 after_selection_start
= webkit_dom_node_contains (
12872 block
, WEBKIT_DOM_NODE (selection_start_marker
));
12873 if (!after_selection_start
)
12877 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph")) {
12878 level
= get_indentation_level (WEBKIT_DOM_ELEMENT (block
));
12880 final_width
= word_wrap_length
- SPACES_PER_INDENTATION
* (level
+ 1);
12881 if (final_width
< MINIMAL_PARAGRAPH_WIDTH
&&
12882 !e_editor_page_get_html_mode (editor_page
))
12886 indent_block (editor_page
, block
, final_width
);
12888 if (after_selection_end
)
12892 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
12893 WebKitDOMNode
*block_to_process
;
12895 block_to_process
= webkit_dom_node_list_item (list
, ii
);
12897 after_selection_end
= webkit_dom_node_contains (
12898 block_to_process
, WEBKIT_DOM_NODE (selection_end_marker
));
12900 if (!after_selection_start
) {
12901 after_selection_start
= webkit_dom_node_contains (
12903 WEBKIT_DOM_NODE (selection_start_marker
));
12904 if (!after_selection_start
)
12908 if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block_to_process
), "data-evo-paragraph")) {
12909 level
= get_indentation_level (
12910 WEBKIT_DOM_ELEMENT (block_to_process
));
12912 final_width
= word_wrap_length
- SPACES_PER_INDENTATION
* (level
+ 1);
12913 if (final_width
< MINIMAL_PARAGRAPH_WIDTH
&&
12914 !e_editor_page_get_html_mode (editor_page
))
12918 indent_block (editor_page
, block_to_process
, final_width
);
12920 if (after_selection_end
)
12925 g_clear_object (&list
);
12927 if (!after_selection_end
)
12928 block
= next_block
;
12932 e_editor_dom_selection_get_coordinates (editor_page
,
12933 &ev
->after
.start
.x
,
12934 &ev
->after
.start
.y
,
12937 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
12940 e_editor_dom_selection_restore (editor_page
);
12941 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
12942 e_editor_page_emit_content_changed (editor_page
);
12946 unindent_list (WebKitDOMDocument
*document
)
12948 gboolean after
= FALSE
;
12949 WebKitDOMElement
*new_list
;
12950 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
12951 WebKitDOMNode
*source_list
, *source_list_clone
, *current_list
, *item
;
12952 WebKitDOMNode
*prev_item
;
12954 selection_start_marker
= webkit_dom_document_get_element_by_id (
12955 document
, "-x-evo-selection-start-marker");
12956 selection_end_marker
= webkit_dom_document_get_element_by_id (
12957 document
, "-x-evo-selection-end-marker");
12959 if (!selection_start_marker
|| !selection_end_marker
)
12962 /* Copy elements from previous block to list */
12963 item
= e_editor_dom_get_parent_block_node_from_child (
12964 WEBKIT_DOM_NODE (selection_start_marker
));
12965 source_list
= webkit_dom_node_get_parent_node (item
);
12966 new_list
= WEBKIT_DOM_ELEMENT (
12967 webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
));
12968 current_list
= source_list
;
12969 source_list_clone
= webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
);
12971 webkit_dom_node_insert_before (
12972 webkit_dom_node_get_parent_node (source_list
),
12973 WEBKIT_DOM_NODE (source_list_clone
),
12974 webkit_dom_node_get_next_sibling (source_list
),
12977 if (element_has_class (WEBKIT_DOM_ELEMENT (source_list
), "-x-evo-indented"))
12978 element_add_class (WEBKIT_DOM_ELEMENT (new_list
), "-x-evo-indented");
12980 prev_item
= source_list
;
12983 WebKitDOMNode
*next_item
= webkit_dom_node_get_next_sibling (item
);
12985 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
12987 prev_item
= webkit_dom_node_append_child (
12988 source_list_clone
, WEBKIT_DOM_NODE (item
), NULL
);
12990 prev_item
= webkit_dom_node_insert_before (
12991 webkit_dom_node_get_parent_node (prev_item
),
12993 webkit_dom_node_get_next_sibling (prev_item
),
12997 if (webkit_dom_node_contains (item
, WEBKIT_DOM_NODE (selection_end_marker
)))
13004 current_list
= webkit_dom_node_get_next_sibling (current_list
);
13005 next_item
= webkit_dom_node_get_first_child (current_list
);
13010 remove_node_if_empty (source_list_clone
);
13011 remove_node_if_empty (source_list
);
13015 unindent_block (EEditorPage
*editor_page
,
13016 WebKitDOMNode
*block
)
13018 WebKitDOMElement
*element
;
13019 WebKitDOMElement
*prev_blockquote
= NULL
, *next_blockquote
= NULL
;
13020 WebKitDOMNode
*block_to_process
, *node_clone
= NULL
, *child
;
13021 EContentEditorAlignment alignment
;
13022 gboolean before_node
= TRUE
;
13023 gint word_wrap_length
, level
, width
;
13025 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13027 block_to_process
= block
;
13029 alignment
= dom_get_alignment_from_node (block_to_process
);
13030 element
= webkit_dom_node_get_parent_element (block_to_process
);
13032 if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element
) &&
13033 !element_has_class (element
, "-x-evo-indented"))
13036 element_add_class (WEBKIT_DOM_ELEMENT (block_to_process
), "-x-evo-to-unindent");
13038 level
= get_indentation_level (element
);
13039 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
13040 width
= word_wrap_length
- SPACES_PER_INDENTATION
* level
;
13042 /* Look if we have previous siblings, if so, we have to
13043 * create new blockquote that will include them */
13044 if (webkit_dom_node_get_previous_sibling (block_to_process
))
13045 prev_blockquote
= dom_get_indented_element (editor_page
, width
);
13047 /* Look if we have next siblings, if so, we have to
13048 * create new blockquote that will include them */
13049 if (webkit_dom_node_get_next_sibling (block_to_process
))
13050 next_blockquote
= dom_get_indented_element (editor_page
, width
);
13052 /* Copy nodes that are before / after the element that we want to unindent */
13053 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
)))) {
13054 if (webkit_dom_node_is_equal_node (child
, block_to_process
)) {
13055 before_node
= FALSE
;
13056 node_clone
= webkit_dom_node_clone_node_with_error (child
, TRUE
, NULL
);
13057 remove_node (child
);
13061 webkit_dom_node_append_child (
13063 WEBKIT_DOM_NODE (prev_blockquote
) :
13064 WEBKIT_DOM_NODE (next_blockquote
),
13070 element_remove_class (WEBKIT_DOM_ELEMENT (node_clone
), "-x-evo-to-unindent");
13072 /* Insert blockqoute with nodes that were before the element that we want to unindent */
13073 if (prev_blockquote
) {
13074 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote
))) {
13075 webkit_dom_node_insert_before (
13076 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13077 WEBKIT_DOM_NODE (prev_blockquote
),
13078 WEBKIT_DOM_NODE (element
),
13083 if (level
== 1 && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node_clone
), "data-evo-paragraph")) {
13084 e_editor_dom_set_paragraph_style (
13085 editor_page
, WEBKIT_DOM_ELEMENT (node_clone
), word_wrap_length
, 0, NULL
);
13086 element_add_class (
13087 WEBKIT_DOM_ELEMENT (node_clone
),
13088 get_css_alignment_value_class (alignment
));
13091 /* Insert the unindented element */
13092 webkit_dom_node_insert_before (
13093 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13095 WEBKIT_DOM_NODE (element
),
13098 g_warn_if_reached ();
13101 /* Insert blockqoute with nodes that were after the element that we want to unindent */
13102 if (next_blockquote
) {
13103 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote
))) {
13104 webkit_dom_node_insert_before (
13105 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
13106 WEBKIT_DOM_NODE (next_blockquote
),
13107 WEBKIT_DOM_NODE (element
),
13112 /* Remove old blockquote */
13113 remove_node (WEBKIT_DOM_NODE (element
));
13118 * @selection: an #EEditorSelection
13120 * Unindents current paragraph by one level.
13123 e_editor_dom_selection_unindent (EEditorPage
*editor_page
)
13125 WebKitDOMDocument
*document
;
13126 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
13127 WebKitDOMNode
*block
;
13128 EEditorHistoryEvent
*ev
= NULL
;
13129 EEditorUndoRedoManager
*manager
;
13130 gboolean after_selection_start
= FALSE
, after_selection_end
= FALSE
;
13132 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13134 document
= e_editor_page_get_document (editor_page
);
13135 e_editor_dom_selection_save (editor_page
);
13137 selection_start_marker
= webkit_dom_document_get_element_by_id (
13138 document
, "-x-evo-selection-start-marker");
13139 selection_end_marker
= webkit_dom_document_get_element_by_id (
13140 document
, "-x-evo-selection-end-marker");
13142 /* If the selection was not saved, move it into the first child of body */
13143 if (!selection_start_marker
|| !selection_end_marker
) {
13144 WebKitDOMHTMLElement
*body
;
13145 WebKitDOMNode
*child
;
13147 body
= webkit_dom_document_get_body (document
);
13148 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
13150 dom_add_selection_markers_into_element_start (
13152 WEBKIT_DOM_ELEMENT (child
),
13153 &selection_start_marker
,
13154 &selection_end_marker
);
13157 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
13158 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
13159 ev
= g_new0 (EEditorHistoryEvent
, 1);
13160 ev
->type
= HISTORY_INDENT
;
13162 e_editor_dom_selection_get_coordinates (editor_page
,
13163 &ev
->before
.start
.x
,
13164 &ev
->before
.start
.y
,
13166 &ev
->before
.end
.y
);
13169 block
= get_parent_indented_block (
13170 WEBKIT_DOM_NODE (selection_start_marker
));
13172 block
= e_editor_dom_get_parent_block_node_from_child (
13173 WEBKIT_DOM_NODE (selection_start_marker
));
13175 while (block
&& !after_selection_end
) {
13177 WebKitDOMNode
*next_block
;
13178 WebKitDOMNodeList
*list
= NULL
;
13180 next_block
= webkit_dom_node_get_next_sibling (block
);
13182 list
= webkit_dom_element_query_selector_all (
13183 WEBKIT_DOM_ELEMENT (block
),
13184 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
13187 after_selection_end
= webkit_dom_node_contains (
13188 block
, WEBKIT_DOM_NODE (selection_end_marker
));
13190 length
= webkit_dom_node_list_get_length (list
);
13191 if (length
== 0 && node_is_list_or_item (block
)) {
13192 unindent_list (document
);
13197 if (!after_selection_start
) {
13198 after_selection_start
= webkit_dom_node_contains (
13199 block
, WEBKIT_DOM_NODE (selection_start_marker
));
13200 if (!after_selection_start
)
13204 unindent_block (editor_page
, block
);
13206 if (after_selection_end
)
13210 for (ii
= 0; ii
< length
; ii
++) {
13211 WebKitDOMNode
*block_to_process
;
13213 block_to_process
= webkit_dom_node_list_item (list
, ii
);
13215 after_selection_end
= webkit_dom_node_contains (
13217 WEBKIT_DOM_NODE (selection_end_marker
));
13219 if (!after_selection_start
) {
13220 after_selection_start
= webkit_dom_node_contains (
13222 WEBKIT_DOM_NODE (selection_start_marker
));
13223 if (!after_selection_start
)
13227 unindent_block (editor_page
, block_to_process
);
13229 if (after_selection_end
)
13233 g_clear_object (&list
);
13234 block
= next_block
;
13238 e_editor_dom_selection_get_coordinates (editor_page
,
13239 &ev
->after
.start
.x
,
13240 &ev
->after
.start
.y
,
13243 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
13246 e_editor_dom_selection_restore (editor_page
);
13248 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
13249 e_editor_page_emit_content_changed (editor_page
);
13253 dom_insert_selection_point (WebKitDOMNode
*container
,
13255 WebKitDOMElement
*selection_point
)
13257 WebKitDOMNode
*parent
;
13259 parent
= webkit_dom_node_get_parent_node (container
);
13261 if (WEBKIT_DOM_IS_TEXT (container
) ||
13262 WEBKIT_DOM_IS_COMMENT (container
) ||
13263 WEBKIT_DOM_IS_CHARACTER_DATA (container
)) {
13265 WebKitDOMText
*split_text
;
13267 split_text
= webkit_dom_text_split_text (
13268 WEBKIT_DOM_TEXT (container
), offset
, NULL
);
13269 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (split_text
));
13271 webkit_dom_node_insert_before (
13273 WEBKIT_DOM_NODE (selection_point
),
13274 WEBKIT_DOM_NODE (split_text
),
13277 webkit_dom_node_insert_before (
13279 WEBKIT_DOM_NODE (selection_point
),
13284 gulong child_element_count
= 0;
13286 child_element_count
=
13287 webkit_dom_element_get_child_element_count (
13288 WEBKIT_DOM_ELEMENT (container
));
13291 /* Selection point is on the beginning of container */
13292 webkit_dom_node_insert_before (
13294 WEBKIT_DOM_NODE (selection_point
),
13295 webkit_dom_node_get_first_child (container
),
13297 } else if (offset
!= 0 && (offset
== child_element_count
)) {
13298 /* Selection point is on the end of container */
13299 webkit_dom_node_append_child (
13300 container
, WEBKIT_DOM_NODE (selection_point
), NULL
);
13302 WebKitDOMElement
*child
;
13305 child
= webkit_dom_element_get_first_element_child (WEBKIT_DOM_ELEMENT (container
));
13306 for (ii
= 1; ii
< child_element_count
; ii
++)
13307 child
= webkit_dom_element_get_next_element_sibling (child
);
13309 webkit_dom_node_insert_before (
13311 WEBKIT_DOM_NODE (selection_point
),
13312 WEBKIT_DOM_NODE (child
),
13317 webkit_dom_node_normalize (parent
);
13321 * e_html_editor_selection_save:
13322 * @selection: an #EEditorSelection
13324 * Saves current cursor position or current selection range. The selection can
13325 * be later restored by calling e_html_editor_selection_restore().
13327 * Note that calling e_html_editor_selection_save() overwrites previously saved
13330 * Note that this method inserts special markings into the HTML code that are
13331 * used to later restore the selection. It can happen that by deleting some
13332 * segments of the document some of the markings are deleted too. In that case
13333 * restoring the selection by e_html_editor_selection_restore() can fail. Also by
13334 * moving text segments (Cut & Paste) can result in moving the markings
13335 * elsewhere, thus e_html_editor_selection_restore() will restore the selection
13338 * It is recommended to use this method only when you are not planning to make
13339 * bigger changes to content or structure of the document (formatting changes
13343 e_editor_dom_selection_save (EEditorPage
*editor_page
)
13345 WebKitDOMDocument
*document
;
13346 WebKitDOMDOMWindow
*dom_window
= NULL
;
13347 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13348 WebKitDOMRange
*range
= NULL
;
13349 WebKitDOMNode
*container
;
13350 WebKitDOMNode
*anchor
;
13351 WebKitDOMElement
*start_marker
= NULL
, *end_marker
= NULL
;
13352 gboolean collapsed
= FALSE
;
13353 glong offset
, anchor_offset
;
13355 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13357 document
= e_editor_page_get_document (editor_page
);
13359 /* First remove all markers (if present) */
13360 dom_remove_selection_markers (document
);
13362 dom_window
= webkit_dom_document_get_default_view (document
);
13363 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
13364 g_clear_object (&dom_window
);
13366 if (webkit_dom_dom_selection_get_range_count (dom_selection
) < 1) {
13367 g_clear_object (&dom_selection
);
13371 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
13373 g_clear_object (&dom_selection
);
13377 anchor
= webkit_dom_dom_selection_get_anchor_node (dom_selection
);
13378 anchor_offset
= webkit_dom_dom_selection_get_anchor_offset (dom_selection
);
13380 collapsed
= webkit_dom_range_get_collapsed (range
, NULL
);
13381 start_marker
= dom_create_selection_marker (document
, TRUE
);
13383 container
= webkit_dom_range_get_start_container (range
, NULL
);
13384 offset
= webkit_dom_range_get_start_offset (range
, NULL
);
13386 if (webkit_dom_node_is_same_node (anchor
, container
) && offset
== anchor_offset
)
13387 webkit_dom_element_set_attribute (start_marker
, "data-anchor", "", NULL
);
13389 dom_insert_selection_point (container
, offset
, start_marker
);
13391 end_marker
= dom_create_selection_marker (document
, FALSE
);
13394 webkit_dom_node_insert_before (
13395 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker
)),
13396 WEBKIT_DOM_NODE (end_marker
),
13397 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker
)),
13402 container
= webkit_dom_range_get_end_container (range
, NULL
);
13403 offset
= webkit_dom_range_get_end_offset (range
, NULL
);
13405 if (webkit_dom_node_is_same_node (anchor
, container
) && offset
== anchor_offset
)
13406 webkit_dom_element_set_attribute (end_marker
, "data-anchor", "", NULL
);
13408 dom_insert_selection_point (container
, offset
, end_marker
);
13411 if (start_marker
&& end_marker
) {
13412 webkit_dom_range_set_start_after (range
, WEBKIT_DOM_NODE (start_marker
), NULL
);
13413 webkit_dom_range_set_end_before (range
, WEBKIT_DOM_NODE (end_marker
), NULL
);
13415 g_warn_if_reached ();
13418 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
13419 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13422 g_clear_object (&range
);
13423 g_clear_object (&dom_selection
);
13427 e_editor_dom_is_selection_position_node (WebKitDOMNode
*node
)
13429 WebKitDOMElement
*element
;
13431 if (!node
|| !WEBKIT_DOM_IS_ELEMENT (node
))
13434 element
= WEBKIT_DOM_ELEMENT (node
);
13436 return element_has_id (element
, "-x-evo-selection-start-marker") ||
13437 element_has_id (element
, "-x-evo-selection-end-marker");
13441 * e_html_editor_selection_restore:
13442 * @selection: an #EEditorSelection
13444 * Restores cursor position or selection range that was saved by
13445 * e_html_editor_selection_save().
13447 * Note that calling this function without calling e_html_editor_selection_save()
13448 * before is a programming error and the behavior is undefined.
13451 e_editor_dom_selection_restore (EEditorPage
*editor_page
)
13453 WebKitDOMDocument
*document
;
13454 WebKitDOMElement
*marker
;
13455 WebKitDOMNode
*selection_start_marker
, *selection_end_marker
;
13456 WebKitDOMNode
*parent_start
, *parent_end
, *anchor
;
13457 WebKitDOMRange
*range
= NULL
;
13458 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13459 WebKitDOMDOMWindow
*dom_window
= NULL
;
13460 gboolean start_is_anchor
= FALSE
;
13463 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13465 document
= e_editor_page_get_document (editor_page
);
13466 dom_window
= webkit_dom_document_get_default_view (document
);
13467 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
13468 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
13469 g_clear_object (&dom_window
);
13471 WebKitDOMHTMLElement
*body
;
13473 range
= webkit_dom_document_create_range (document
);
13474 body
= webkit_dom_document_get_body (document
);
13476 webkit_dom_range_select_node_contents (range
, WEBKIT_DOM_NODE (body
), NULL
);
13477 webkit_dom_range_collapse (range
, TRUE
, NULL
);
13478 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13481 selection_start_marker
= webkit_dom_range_get_start_container (range
, NULL
);
13482 if (selection_start_marker
) {
13483 gboolean ok
= FALSE
;
13484 selection_start_marker
=
13485 webkit_dom_node_get_next_sibling (selection_start_marker
);
13487 ok
= e_editor_dom_is_selection_position_node (selection_start_marker
);
13491 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
13492 selection_end_marker
= webkit_dom_node_get_next_sibling (
13493 selection_start_marker
);
13495 ok
= e_editor_dom_is_selection_position_node (selection_end_marker
);
13497 WebKitDOMNode
*next_sibling
;
13499 next_sibling
= webkit_dom_node_get_next_sibling (selection_end_marker
);
13501 if (next_sibling
&& !WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling
)) {
13502 parent_start
= webkit_dom_node_get_parent_node (selection_end_marker
);
13504 remove_node (selection_start_marker
);
13505 remove_node (selection_end_marker
);
13507 webkit_dom_node_normalize (parent_start
);
13508 g_clear_object (&range
);
13509 g_clear_object (&dom_selection
);
13517 g_clear_object (&range
);
13518 range
= webkit_dom_document_create_range (document
);
13520 g_clear_object (&dom_selection
);
13524 marker
= webkit_dom_document_get_element_by_id (
13525 document
, "-x-evo-selection-start-marker");
13527 marker
= webkit_dom_document_get_element_by_id (
13528 document
, "-x-evo-selection-end-marker");
13530 remove_node (WEBKIT_DOM_NODE (marker
));
13531 g_clear_object (&dom_selection
);
13532 g_clear_object (&range
);
13536 start_is_anchor
= webkit_dom_element_has_attribute (marker
, "data-anchor");
13537 parent_start
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker
));
13539 webkit_dom_range_set_start_after (range
, WEBKIT_DOM_NODE (marker
), NULL
);
13540 remove_node (WEBKIT_DOM_NODE (marker
));
13542 marker
= webkit_dom_document_get_element_by_id (
13543 document
, "-x-evo-selection-end-marker");
13545 marker
= webkit_dom_document_get_element_by_id (
13546 document
, "-x-evo-selection-start-marker");
13548 remove_node (WEBKIT_DOM_NODE (marker
));
13549 g_clear_object (&dom_selection
);
13550 g_clear_object (&range
);
13554 parent_end
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker
));
13556 webkit_dom_range_set_end_before (range
, WEBKIT_DOM_NODE (marker
), NULL
);
13557 remove_node (WEBKIT_DOM_NODE (marker
));
13559 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
13560 if (webkit_dom_node_is_same_node (parent_start
, parent_end
))
13561 webkit_dom_node_normalize (parent_start
);
13563 webkit_dom_node_normalize (parent_start
);
13564 webkit_dom_node_normalize (parent_end
);
13567 if (start_is_anchor
) {
13568 anchor
= webkit_dom_range_get_end_container (range
, NULL
);
13569 offset
= webkit_dom_range_get_end_offset (range
, NULL
);
13571 webkit_dom_range_collapse (range
, TRUE
, NULL
);
13573 anchor
= webkit_dom_range_get_start_container (range
, NULL
);
13574 offset
= webkit_dom_range_get_start_offset (range
, NULL
);
13576 webkit_dom_range_collapse (range
, FALSE
, NULL
);
13578 webkit_dom_dom_selection_add_range (dom_selection
, range
);
13579 webkit_dom_dom_selection_extend (dom_selection
, anchor
, offset
, NULL
);
13581 g_clear_object (&dom_selection
);
13582 g_clear_object (&range
);
13586 find_where_to_break_line (WebKitDOMCharacterData
*node
,
13589 gboolean last_break_position_is_dash
= FALSE
;
13590 gchar
*str
, *text_start
;
13592 gint pos
= 1, last_break_position
= 0, ret_val
= 0;
13594 text_start
= webkit_dom_character_data_get_data (node
);
13598 uc
= g_utf8_get_char (str
);
13600 ret_val
= pos
<= max_length
? pos
: last_break_position
> 0 ? last_break_position
- 1 : 0;
13604 if ((g_unichar_isspace (uc
) && !(g_unichar_break_type (uc
) == G_UNICODE_BREAK_NON_BREAKING_GLUE
)) ||
13606 if ((last_break_position_is_dash
= *str
== '-')) {
13607 /* There was no space before the dash */
13608 if (pos
- 1 != last_break_position
) {
13611 rest
= g_utf8_next_char (str
);
13612 if (rest
&& *rest
) {
13613 gunichar next_char
;
13615 /* There is no space after the dash */
13616 next_char
= g_utf8_get_char (rest
);
13617 if (g_unichar_isspace (next_char
))
13618 last_break_position_is_dash
= FALSE
;
13620 last_break_position
= pos
;
13622 last_break_position_is_dash
= FALSE
;
13624 last_break_position_is_dash
= FALSE
;
13626 last_break_position
= pos
;
13629 if ((pos
== max_length
)) {
13630 /* Look one character after the limit to check if there
13631 * is a space (skip dash) that we are allowed to break at, if so
13632 * break it there. */
13634 str
= g_utf8_next_char (str
);
13635 uc
= g_utf8_get_char (str
);
13637 if ((g_unichar_isspace (uc
) &&
13638 !(g_unichar_break_type (uc
) == G_UNICODE_BREAK_NON_BREAKING_GLUE
)))
13639 last_break_position
= ++pos
;
13645 str
= g_utf8_next_char (str
);
13648 if (last_break_position
!= 0)
13649 ret_val
= last_break_position
- 1;
13651 g_free (text_start
);
13653 /* Always break after the dash character. */
13654 if (last_break_position_is_dash
)
13657 /* No character to break at is found. We should split at max_length, but
13658 * we will leave the decision on caller as it depends on context. */
13659 if (ret_val
== 0 && last_break_position
== 0)
13666 * e_html_editor_selection_is_collapsed:
13667 * @selection: an #EEditorSelection
13669 * Returns if selection is collapsed.
13671 * Returns: Whether the selection is collapsed (just caret) or not (someting is selected).
13674 e_editor_dom_selection_is_collapsed (EEditorPage
*editor_page
)
13676 WebKitDOMDocument
*document
;
13677 WebKitDOMDOMWindow
*dom_window
= NULL
;
13678 WebKitDOMDOMSelection
*dom_selection
= NULL
;
13679 gboolean collapsed
;
13681 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
13683 document
= e_editor_page_get_document (editor_page
);
13684 if (!(dom_window
= webkit_dom_document_get_default_view (document
)))
13687 if (!(dom_selection
= webkit_dom_dom_window_get_selection (dom_window
))) {
13688 g_clear_object (&dom_window
);
13692 collapsed
= webkit_dom_dom_selection_get_is_collapsed (dom_selection
);
13694 g_clear_object (&dom_selection
);
13700 e_editor_dom_scroll_to_caret (EEditorPage
*editor_page
)
13702 WebKitDOMDocument
*document
;
13703 WebKitDOMDOMWindow
*dom_window
= NULL
;
13704 WebKitDOMElement
*selection_start_marker
;
13705 glong element_top
, element_left
;
13706 glong window_top
, window_left
, window_right
, window_bottom
;
13708 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
13710 document
= e_editor_page_get_document (editor_page
);
13711 e_editor_dom_selection_save (editor_page
);
13713 selection_start_marker
= webkit_dom_document_get_element_by_id (
13714 document
, "-x-evo-selection-start-marker");
13715 if (!selection_start_marker
)
13718 dom_window
= webkit_dom_document_get_default_view (document
);
13720 window_top
= webkit_dom_dom_window_get_scroll_y (dom_window
);
13721 window_left
= webkit_dom_dom_window_get_scroll_x (dom_window
);
13722 window_bottom
= window_top
+ webkit_dom_dom_window_get_inner_height (dom_window
);
13723 window_right
= window_left
+ webkit_dom_dom_window_get_inner_width (dom_window
);
13725 element_left
= webkit_dom_element_get_offset_left (selection_start_marker
);
13726 element_top
= webkit_dom_element_get_offset_top (selection_start_marker
);
13728 /* Check if caret is inside viewport, if not move to it */
13729 if (!(element_top
>= window_top
&& element_top
<= window_bottom
&&
13730 element_left
>= window_left
&& element_left
<= window_right
)) {
13731 webkit_dom_element_scroll_into_view (selection_start_marker
, TRUE
);
13734 e_editor_dom_selection_restore (editor_page
);
13736 g_clear_object (&dom_window
);
13740 mark_and_remove_trailing_space (WebKitDOMDocument
*document
,
13741 WebKitDOMNode
*node
)
13743 WebKitDOMElement
*element
;
13745 element
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
13746 webkit_dom_element_set_attribute (element
, "data-hidden-space", "", NULL
);
13747 webkit_dom_node_insert_before (
13748 webkit_dom_node_get_parent_node (node
),
13749 WEBKIT_DOM_NODE (element
),
13750 webkit_dom_node_get_next_sibling (node
),
13752 webkit_dom_character_data_replace_data (
13753 WEBKIT_DOM_CHARACTER_DATA (node
),
13754 webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node
)),
13761 mark_and_remove_leading_space (WebKitDOMDocument
*document
,
13762 WebKitDOMNode
*node
)
13764 WebKitDOMElement
*element
;
13766 element
= webkit_dom_document_create_element (document
, "SPAN", NULL
);
13767 webkit_dom_element_set_attribute (element
, "data-hidden-space", "", NULL
);
13768 webkit_dom_node_insert_before (
13769 webkit_dom_node_get_parent_node (node
),
13770 WEBKIT_DOM_NODE (element
),
13773 webkit_dom_character_data_replace_data (
13774 WEBKIT_DOM_CHARACTER_DATA (node
), 0, 1, "", NULL
);
13777 static WebKitDOMElement
*
13778 wrap_lines (EEditorPage
*editor_page
,
13779 WebKitDOMNode
*block
,
13780 gboolean remove_all_br
,
13781 gint length_to_wrap
,
13782 gint word_wrap_length
)
13784 WebKitDOMDocument
*document
;
13785 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
13786 WebKitDOMNode
*node
, *start_node
, *block_clone
= NULL
;
13787 WebKitDOMNode
*start_point
= NULL
, *first_child
, *last_child
;
13789 gulong length_left
;
13790 gchar
*text_content
;
13791 gboolean compensated
= FALSE
;
13792 gboolean check_next_node
= FALSE
;
13794 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
13796 document
= e_editor_page_get_document (editor_page
);
13798 if (!webkit_dom_node_has_child_nodes (block
))
13799 return WEBKIT_DOM_ELEMENT (block
);
13801 /* Avoid wrapping when the block contains just the BR element alone
13802 * or with selection markers. */
13803 if ((first_child
= webkit_dom_node_get_first_child (block
)) &&
13804 WEBKIT_DOM_IS_HTML_BR_ELEMENT (first_child
)) {
13805 WebKitDOMNode
*next_sibling
;
13807 if ((next_sibling
= webkit_dom_node_get_next_sibling (first_child
))) {
13808 if (e_editor_dom_is_selection_position_node (next_sibling
) &&
13809 (next_sibling
= webkit_dom_node_get_next_sibling (next_sibling
)) &&
13810 e_editor_dom_is_selection_position_node (next_sibling
) &&
13811 !webkit_dom_node_get_next_sibling (next_sibling
))
13812 return WEBKIT_DOM_ELEMENT (block
);
13814 return WEBKIT_DOM_ELEMENT (block
);
13817 block_clone
= webkit_dom_node_clone_node_with_error (block
, TRUE
, NULL
);
13819 /* When we wrap, we are wrapping just the text after caret, text
13820 * before the caret is already wrapped, so unwrap the text after
13821 * the caret position */
13822 selection_end_marker
= webkit_dom_element_query_selector (
13823 WEBKIT_DOM_ELEMENT (block_clone
),
13824 "span#-x-evo-selection-end-marker",
13827 if (selection_end_marker
) {
13828 WebKitDOMNode
*nd
= WEBKIT_DOM_NODE (selection_end_marker
);
13831 WebKitDOMNode
*parent_node
;
13832 WebKitDOMNode
*next_nd
= webkit_dom_node_get_next_sibling (nd
);
13834 parent_node
= webkit_dom_node_get_parent_node (nd
);
13835 if (!next_nd
&& parent_node
&& !webkit_dom_node_is_same_node (parent_node
, block_clone
))
13836 next_nd
= webkit_dom_node_get_next_sibling (parent_node
);
13838 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd
)) {
13841 else if (element_has_class (WEBKIT_DOM_ELEMENT (nd
), "-x-evo-wrap-br"))
13843 } else if (WEBKIT_DOM_IS_ELEMENT (nd
) &&
13844 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd
), "data-hidden-space"))
13845 webkit_dom_html_element_set_outer_text (
13846 WEBKIT_DOM_HTML_ELEMENT (nd
), " ", NULL
);
13852 WebKitDOMNodeList
*list
= NULL
;
13854 list
= webkit_dom_element_query_selector_all (
13855 WEBKIT_DOM_ELEMENT (block_clone
), "span[data-hidden-space]", NULL
);
13856 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
13857 WebKitDOMNode
*hidden_space_node
;
13859 hidden_space_node
= webkit_dom_node_list_item (list
, ii
);
13860 webkit_dom_html_element_set_outer_text (
13861 WEBKIT_DOM_HTML_ELEMENT (hidden_space_node
), " ", NULL
);
13863 g_clear_object (&list
);
13866 /* We have to start from the end of the last wrapped line */
13867 selection_start_marker
= webkit_dom_element_query_selector (
13868 WEBKIT_DOM_ELEMENT (block_clone
),
13869 "span#-x-evo-selection-start-marker",
13872 if (selection_start_marker
) {
13873 gboolean first_removed
= FALSE
;
13876 nd
= webkit_dom_node_get_previous_sibling (
13877 WEBKIT_DOM_NODE (selection_start_marker
));
13879 WebKitDOMNode
*prev_nd
= webkit_dom_node_get_previous_sibling (nd
);
13881 if (!prev_nd
&& !webkit_dom_node_is_same_node (webkit_dom_node_get_parent_node (nd
), block_clone
))
13882 prev_nd
= webkit_dom_node_get_previous_sibling (webkit_dom_node_get_parent_node (nd
));
13884 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd
)) {
13885 if (first_removed
) {
13890 first_removed
= TRUE
;
13892 } else if (WEBKIT_DOM_IS_ELEMENT (nd
) &&
13893 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd
), "data-hidden-space")) {
13894 webkit_dom_html_element_set_outer_text (
13895 WEBKIT_DOM_HTML_ELEMENT (nd
), " ", NULL
);
13896 } else if (!prev_nd
) {
13897 WebKitDOMNode
*parent
;
13899 parent
= webkit_dom_node_get_parent_node (nd
);
13900 if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent
))
13908 webkit_dom_node_normalize (block_clone
);
13909 node
= webkit_dom_node_get_first_child (block_clone
);
13911 text_content
= webkit_dom_node_get_text_content (node
);
13912 if (g_strcmp0 ("\n", text_content
) == 0)
13913 node
= webkit_dom_node_get_next_sibling (node
);
13914 g_free (text_content
);
13918 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (start_point
))
13919 node
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_point
));
13921 node
= start_point
;
13922 start_node
= block_clone
;
13929 WebKitDOMElement
*element
;
13931 if (WEBKIT_DOM_IS_TEXT (node
)) {
13932 const gchar
*newline
;
13933 WebKitDOMNode
*next_sibling
;
13935 /* If there is temporary hidden space we remove it */
13936 text_content
= webkit_dom_node_get_text_content (node
);
13937 if (strstr (text_content
, UNICODE_ZERO_WIDTH_SPACE
)) {
13938 if (g_str_has_prefix (text_content
, UNICODE_ZERO_WIDTH_SPACE
))
13939 webkit_dom_character_data_delete_data (
13940 WEBKIT_DOM_CHARACTER_DATA (node
),
13944 if (g_str_has_suffix (text_content
, UNICODE_ZERO_WIDTH_SPACE
))
13945 webkit_dom_character_data_delete_data (
13946 WEBKIT_DOM_CHARACTER_DATA (node
),
13947 g_utf8_strlen (text_content
, -1) - 1,
13950 g_free (text_content
);
13951 text_content
= webkit_dom_node_get_text_content (node
);
13953 newline
= strstr (text_content
, "\n");
13955 next_sibling
= node
;
13957 next_sibling
= WEBKIT_DOM_NODE (webkit_dom_text_split_text (
13958 WEBKIT_DOM_TEXT (next_sibling
),
13959 g_utf8_pointer_to_offset (text_content
, newline
),
13965 element
= webkit_dom_document_create_element (
13966 document
, "BR", NULL
);
13967 element_add_class (element
, "-x-evo-wrap-br");
13969 webkit_dom_node_insert_before (
13970 webkit_dom_node_get_parent_node (next_sibling
),
13971 WEBKIT_DOM_NODE (element
),
13975 g_free (text_content
);
13977 text_content
= webkit_dom_node_get_text_content (next_sibling
);
13978 if (g_str_has_prefix (text_content
, "\n")) {
13979 webkit_dom_character_data_delete_data (
13980 WEBKIT_DOM_CHARACTER_DATA (next_sibling
), 0, 1, NULL
);
13981 g_free (text_content
);
13983 webkit_dom_node_get_text_content (next_sibling
);
13985 newline
= strstr (text_content
, "\n");
13987 g_free (text_content
);
13988 } else if (WEBKIT_DOM_IS_ELEMENT (node
)) {
13989 if (e_editor_dom_is_selection_position_node (node
)) {
13990 if (line_length
== 0) {
13991 WebKitDOMNode
*tmp_node
;
13993 tmp_node
= webkit_dom_node_get_previous_sibling (node
);
13994 /* Only check if there is some node before the selection marker. */
13995 if (tmp_node
&& !e_editor_dom_is_selection_position_node (tmp_node
))
13996 check_next_node
= TRUE
;
13998 node
= webkit_dom_node_get_next_sibling (node
);
14002 check_next_node
= FALSE
;
14003 /* If element is ANCHOR we wrap it separately */
14004 if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node
)) {
14005 glong anchor_length
;
14006 WebKitDOMNode
*next_sibling
;
14008 text_content
= webkit_dom_node_get_text_content (node
);
14009 anchor_length
= g_utf8_strlen (text_content
, -1);
14010 g_free (text_content
);
14012 next_sibling
= webkit_dom_node_get_next_sibling (node
);
14013 /* If the anchor doesn't fit on the line move the inner
14014 * nodes out of it and start to wrap them. */
14015 if ((line_length
+ anchor_length
) > length_to_wrap
) {
14016 WebKitDOMNode
*inner_node
;
14018 while ((inner_node
= webkit_dom_node_get_first_child (node
))) {
14019 g_object_set_data (
14020 G_OBJECT (inner_node
),
14021 "-x-evo-anchor-text",
14022 GINT_TO_POINTER (1));
14023 webkit_dom_node_insert_before (
14024 webkit_dom_node_get_parent_node (node
),
14029 next_sibling
= webkit_dom_node_get_next_sibling (node
);
14031 remove_node (node
);
14032 node
= next_sibling
;
14036 line_length
+= anchor_length
;
14037 node
= next_sibling
;
14041 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "Apple-tab-span")) {
14042 WebKitDOMNode
*sibling
;
14045 sibling
= webkit_dom_node_get_previous_sibling (node
);
14046 if (sibling
&& WEBKIT_DOM_IS_ELEMENT (sibling
) &&
14047 element_has_class (WEBKIT_DOM_ELEMENT (sibling
), "Apple-tab-span"))
14048 tab_length
= TAB_LENGTH
;
14050 tab_length
= TAB_LENGTH
- (line_length
+ compensated
? 0 : (word_wrap_length
- length_to_wrap
)) % TAB_LENGTH
;
14051 compensated
= TRUE
;
14054 if (line_length
+ tab_length
> length_to_wrap
) {
14055 if (webkit_dom_node_get_next_sibling (node
)) {
14056 element
= webkit_dom_document_create_element (
14057 document
, "BR", NULL
);
14058 element_add_class (element
, "-x-evo-wrap-br");
14059 node
= webkit_dom_node_insert_before (
14060 webkit_dom_node_get_parent_node (node
),
14061 WEBKIT_DOM_NODE (element
),
14062 webkit_dom_node_get_next_sibling (node
),
14066 compensated
= FALSE
;
14068 line_length
+= tab_length
;
14070 sibling
= webkit_dom_node_get_next_sibling (node
);
14074 /* When we are not removing user-entered BR elements (lines wrapped by user),
14075 * we need to skip those elements */
14076 if (!remove_all_br
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (node
)) {
14077 if (!element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-wrap-br")) {
14079 compensated
= FALSE
;
14080 node
= webkit_dom_node_get_next_sibling (node
);
14086 WebKitDOMNode
*sibling
;
14088 sibling
= webkit_dom_node_get_next_sibling (node
);
14093 /* If length of this node + what we already have is still less
14094 * then length_to_wrap characters, then just concatenate it and
14095 * continue to next node */
14096 length_left
= webkit_dom_character_data_get_length (
14097 WEBKIT_DOM_CHARACTER_DATA (node
));
14099 if ((length_left
+ line_length
) <= length_to_wrap
) {
14100 if (check_next_node
)
14102 line_length
+= length_left
;
14103 if (line_length
== length_to_wrap
) {
14106 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14107 element_add_class (element
, "-x-evo-wrap-br");
14109 webkit_dom_node_insert_before (
14110 webkit_dom_node_get_parent_node (node
),
14111 WEBKIT_DOM_NODE (element
),
14112 webkit_dom_node_get_next_sibling (node
),
14118 /* wrap until we have something */
14119 while (node
&& (length_left
+ line_length
) > length_to_wrap
) {
14120 gboolean insert_and_continue
;
14124 insert_and_continue
= FALSE
;
14126 if (!WEBKIT_DOM_IS_CHARACTER_DATA (node
))
14129 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
14130 element_add_class (element
, "-x-evo-wrap-br");
14132 max_length
= length_to_wrap
- line_length
;
14133 if (max_length
< 0)
14134 max_length
= length_to_wrap
;
14135 else if (max_length
== 0) {
14136 if (check_next_node
) {
14137 insert_and_continue
= TRUE
;
14141 /* Break before the current node and continue. */
14142 webkit_dom_node_insert_before (
14143 webkit_dom_node_get_parent_node (node
),
14144 WEBKIT_DOM_NODE (element
),
14151 /* Allow anchors to break on any character. */
14152 if (g_object_steal_data (G_OBJECT (node
), "-x-evo-anchor-text"))
14153 offset
= max_length
;
14155 /* Find where we can line-break the node so that it
14156 * effectively fills the rest of current row. */
14157 offset
= find_where_to_break_line (
14158 WEBKIT_DOM_CHARACTER_DATA (node
), max_length
);
14160 /* When pressing delete on the end of line to concatenate
14161 * the last word from the line and first word from the
14162 * next line we will end with the second word split
14163 * somewhere in the middle (to be precise it will be
14164 * split after the last character that will fit on the
14165 * previous line. To avoid that we need to put the
14166 * concatenated word on the next line. */
14167 if (offset
== -1 || check_next_node
) {
14168 WebKitDOMNode
*prev_sibling
;
14171 check_next_node
= FALSE
;
14172 prev_sibling
= webkit_dom_node_get_previous_sibling (node
);
14173 if (prev_sibling
&& e_editor_dom_is_selection_position_node (prev_sibling
)) {
14174 WebKitDOMNode
*prev_br
= NULL
;
14176 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14178 /* Collapsed selection */
14179 if (prev_sibling
&& e_editor_dom_is_selection_position_node (prev_sibling
))
14180 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14182 if (prev_sibling
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling
) &&
14183 element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling
), "-x-evo-wrap-br")) {
14184 prev_br
= prev_sibling
;
14185 prev_sibling
= webkit_dom_node_get_previous_sibling (prev_sibling
);
14188 if (prev_sibling
&& WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling
)) {
14190 glong text_length
, length
= 0;
14192 data
= webkit_dom_character_data_get_data (
14193 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
14194 text_length
= webkit_dom_character_data_get_length (
14195 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
14197 /* Find the last character where we can break. */
14198 while (text_length
- length
> 0) {
14199 if (strchr (" ", data
[text_length
- length
- 1])) {
14202 } else if (data
[text_length
- length
- 1] == '-' &&
14203 text_length
- length
> 1 &&
14204 !strchr (" ", data
[text_length
- length
- 2]))
14209 if (text_length
!= length
) {
14212 webkit_dom_text_split_text (
14213 WEBKIT_DOM_TEXT (prev_sibling
),
14214 text_length
- length
,
14217 if ((nd
= webkit_dom_node_get_next_sibling (prev_sibling
))) {
14220 nd_content
= webkit_dom_node_get_text_content (nd
);
14221 if (nd_content
&& *nd_content
) {
14222 if (*nd_content
== ' ')
14223 mark_and_remove_leading_space (document
, nd
);
14225 if (!webkit_dom_node_get_next_sibling (nd
) &&
14226 g_str_has_suffix (nd_content
, " "))
14227 mark_and_remove_trailing_space (document
, nd
);
14229 g_free (nd_content
);
14234 remove_node (prev_br
);
14235 webkit_dom_node_insert_before (
14236 webkit_dom_node_get_parent_node (nd
),
14237 WEBKIT_DOM_NODE (element
),
14242 line_length
= length
;
14249 if (insert_and_continue
) {
14250 webkit_dom_node_insert_before (
14251 webkit_dom_node_get_parent_node (node
),
14252 WEBKIT_DOM_NODE (element
),
14258 insert_and_continue
= FALSE
;
14262 offset
= offset
!= -1 ? offset
: max_length
;
14269 if (offset
!= length_left
&& offset
!= 0) {
14270 webkit_dom_text_split_text (
14271 WEBKIT_DOM_TEXT (node
), offset
, NULL
);
14273 nd
= webkit_dom_node_get_next_sibling (node
);
14278 gboolean no_sibling
= FALSE
;
14281 nd_content
= webkit_dom_node_get_text_content (nd
);
14282 if (nd_content
&& *nd_content
) {
14283 if (*nd_content
== ' ')
14284 mark_and_remove_leading_space (document
, nd
);
14286 if (!webkit_dom_node_get_next_sibling (nd
) &&
14287 length_left
<= length_to_wrap
&&
14288 g_str_has_suffix (nd_content
, " ")) {
14289 mark_and_remove_trailing_space (document
, nd
);
14293 g_free (nd_content
);
14297 webkit_dom_node_insert_before (
14298 webkit_dom_node_get_parent_node (node
),
14299 WEBKIT_DOM_NODE (element
),
14305 nd_content
= webkit_dom_node_get_text_content (nd
);
14308 g_free (nd_content
);
14315 node
= webkit_dom_node_get_next_sibling (
14316 WEBKIT_DOM_NODE (element
));
14319 node
= webkit_dom_node_get_next_sibling (node
);
14324 webkit_dom_node_append_child (
14325 webkit_dom_node_get_parent_node (node
),
14326 WEBKIT_DOM_NODE (element
),
14330 if (node
&& WEBKIT_DOM_IS_CHARACTER_DATA (node
))
14331 length_left
= webkit_dom_character_data_get_length (
14332 WEBKIT_DOM_CHARACTER_DATA (node
));
14335 compensated
= FALSE
;
14337 line_length
+= length_left
- offset
;
14342 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node
)) {
14344 compensated
= FALSE
;
14347 /* Move to next node */
14348 if (webkit_dom_node_has_child_nodes (node
)) {
14349 node
= webkit_dom_node_get_first_child (node
);
14350 } else if (webkit_dom_node_get_next_sibling (node
)) {
14351 node
= webkit_dom_node_get_next_sibling (node
);
14353 WebKitDOMNode
*tmp_parent
;
14355 if (webkit_dom_node_is_equal_node (node
, start_node
))
14358 /* Find a next node that we can process. */
14359 tmp_parent
= webkit_dom_node_get_parent_node (node
);
14360 if (tmp_parent
&& webkit_dom_node_get_next_sibling (tmp_parent
))
14361 node
= webkit_dom_node_get_next_sibling (tmp_parent
);
14363 WebKitDOMNode
*tmp
;
14366 /* Find a node that is not a start node (that would mean
14367 * that we already processed the whole block) and it has
14368 * a sibling that we can process. */
14369 while (tmp
&& !webkit_dom_node_is_equal_node (tmp
, start_node
) &&
14370 !webkit_dom_node_get_next_sibling (tmp
)) {
14371 tmp
= webkit_dom_node_get_parent_node (tmp
);
14374 /* If we found a node to process, let's process its
14375 * sibling, otherwise give up. */
14377 node
= webkit_dom_node_get_next_sibling (tmp
);
14384 last_child
= webkit_dom_node_get_last_child (block_clone
);
14385 if (last_child
&& WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child
) &&
14386 element_has_class (WEBKIT_DOM_ELEMENT (last_child
), "-x-evo-wrap-br"))
14387 remove_node (last_child
);
14389 webkit_dom_node_normalize (block_clone
);
14391 node
= webkit_dom_node_get_parent_node (block
);
14393 /* Replace block with wrapped one */
14394 webkit_dom_node_replace_child (
14395 node
, block_clone
, block
, NULL
);
14398 return WEBKIT_DOM_ELEMENT (block_clone
);
14402 e_editor_dom_remove_wrapping_from_element (WebKitDOMElement
*element
)
14404 WebKitDOMNodeList
*list
= NULL
;
14407 g_return_if_fail (element
!= NULL
);
14409 list
= webkit_dom_element_query_selector_all (
14410 element
, "br.-x-evo-wrap-br", NULL
);
14411 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14412 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
14413 WebKitDOMNode
*parent
;
14415 parent
= e_editor_dom_get_parent_block_node_from_child (node
);
14416 if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "data-user-wrapped"))
14417 remove_node (node
);
14420 g_clear_object (&list
);
14422 list
= webkit_dom_element_query_selector_all (
14423 element
, "span[data-hidden-space]", NULL
);
14424 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14425 WebKitDOMNode
*hidden_space_node
;
14426 WebKitDOMNode
*parent
;
14428 hidden_space_node
= webkit_dom_node_list_item (list
, ii
);
14429 parent
= e_editor_dom_get_parent_block_node_from_child (hidden_space_node
);
14430 if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent
), "data-user-wrapped")) {
14431 webkit_dom_html_element_set_outer_text (
14432 WEBKIT_DOM_HTML_ELEMENT (hidden_space_node
), " ", NULL
);
14435 g_clear_object (&list
);
14437 webkit_dom_node_normalize (WEBKIT_DOM_NODE (element
));
14441 e_editor_dom_remove_quoting_from_element (WebKitDOMElement
*element
)
14444 WebKitDOMHTMLCollection
*collection
= NULL
;
14446 g_return_if_fail (element
!= NULL
);
14448 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
14449 element
, "-x-evo-quoted");
14450 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;)
14451 remove_node (webkit_dom_html_collection_item (collection
, ii
));
14452 g_clear_object (&collection
);
14454 collection
= webkit_dom_element_get_elements_by_class_name_as_html_collection (
14455 element
, "-x-evo-temp-br");
14456 for (ii
= webkit_dom_html_collection_get_length (collection
); ii
--;)
14457 remove_node (webkit_dom_html_collection_item (collection
, ii
));
14458 g_clear_object (&collection
);
14460 webkit_dom_node_normalize (WEBKIT_DOM_NODE (element
));
14464 e_editor_dom_get_paragraph_element (EEditorPage
*editor_page
,
14468 WebKitDOMDocument
*document
;
14469 WebKitDOMElement
*element
;
14471 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14473 document
= e_editor_page_get_document (editor_page
);
14474 element
= webkit_dom_document_create_element (document
, "DIV", NULL
);
14475 e_editor_dom_set_paragraph_style (editor_page
, element
, width
, offset
, NULL
);
14481 e_editor_dom_put_node_into_paragraph (EEditorPage
*editor_page
,
14482 WebKitDOMNode
*node
,
14483 gboolean with_input
)
14485 WebKitDOMDocument
*document
;
14486 WebKitDOMRange
*range
= NULL
;
14487 WebKitDOMElement
*container
;
14489 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14491 document
= e_editor_page_get_document (editor_page
);
14492 range
= webkit_dom_document_create_range (document
);
14493 container
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
14494 webkit_dom_range_select_node (range
, node
, NULL
);
14495 webkit_dom_range_surround_contents (range
, WEBKIT_DOM_NODE (container
), NULL
);
14496 /* We have to move caret position inside this container */
14498 dom_add_selection_markers_into_element_end (document
, container
, NULL
, NULL
);
14500 g_clear_object (&range
);
14506 e_editor_dom_wrap_paragraph_length (EEditorPage
*editor_page
,
14507 WebKitDOMElement
*paragraph
,
14510 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14511 g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph
), NULL
);
14512 g_return_val_if_fail (length
>= MINIMAL_PARAGRAPH_WIDTH
, NULL
);
14514 return wrap_lines (editor_page
, WEBKIT_DOM_NODE (paragraph
), FALSE
, length
,
14515 e_editor_page_get_word_wrap_length (editor_page
));
14519 * e_html_editor_selection_wrap_lines:
14520 * @selection: an #EEditorSelection
14522 * Wraps all lines in current selection to be 71 characters long.
14526 e_editor_dom_selection_wrap (EEditorPage
*editor_page
)
14528 WebKitDOMDocument
*document
;
14529 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
14530 WebKitDOMNode
*block
, *next_block
;
14531 EEditorHistoryEvent
*ev
= NULL
;
14532 EEditorUndoRedoManager
*manager
;
14533 gboolean after_selection_end
= FALSE
, html_mode
;
14534 gint word_wrap_length
;
14536 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
14538 document
= e_editor_page_get_document (editor_page
);
14539 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14541 e_editor_dom_selection_save (editor_page
);
14542 selection_start_marker
= webkit_dom_document_get_element_by_id (
14543 document
, "-x-evo-selection-start-marker");
14544 selection_end_marker
= webkit_dom_document_get_element_by_id (
14545 document
, "-x-evo-selection-end-marker");
14547 /* If the selection was not saved, move it into the first child of body */
14548 if (!selection_start_marker
|| !selection_end_marker
) {
14549 WebKitDOMHTMLElement
*body
;
14550 WebKitDOMNode
*child
;
14552 body
= webkit_dom_document_get_body (document
);
14553 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
14555 dom_add_selection_markers_into_element_start (
14557 WEBKIT_DOM_ELEMENT (child
),
14558 &selection_start_marker
,
14559 &selection_end_marker
);
14562 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
14563 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
14564 ev
= g_new0 (EEditorHistoryEvent
, 1);
14565 ev
->type
= HISTORY_WRAP
;
14567 e_editor_dom_selection_get_coordinates (editor_page
,
14568 &ev
->before
.start
.x
,
14569 &ev
->before
.start
.y
,
14571 &ev
->before
.end
.y
);
14573 ev
->data
.style
.from
= 1;
14574 ev
->data
.style
.to
= 1;
14577 block
= e_editor_dom_get_parent_block_node_from_child (
14578 WEBKIT_DOM_NODE (selection_start_marker
));
14580 html_mode
= e_editor_page_get_html_mode (editor_page
);
14582 /* Process all blocks that are in the selection one by one */
14583 while (block
&& !after_selection_end
) {
14584 gboolean quoted
= FALSE
;
14585 gint citation_level
, quote
;
14586 WebKitDOMElement
*wrapped_paragraph
;
14588 next_block
= webkit_dom_node_get_next_sibling (block
);
14590 /* Don't try to wrap the 'Normal' blocks as they are already wrapped and*/
14591 /* also skip blocks that we already wrapped with this function. */
14592 if ((!html_mode
&& webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-evo-paragraph")) ||
14593 webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block
), "data-user-wrapped")) {
14594 block
= next_block
;
14598 if (webkit_dom_element_query_selector (
14599 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
14601 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
14605 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
14607 after_selection_end
= webkit_dom_node_contains (
14608 block
, WEBKIT_DOM_NODE (selection_end_marker
));
14610 citation_level
= e_editor_dom_get_citation_level (block
);
14611 quote
= citation_level
? citation_level
* 2 : 0;
14613 wrapped_paragraph
= e_editor_dom_wrap_paragraph_length (
14614 editor_page
, WEBKIT_DOM_ELEMENT (block
), word_wrap_length
- quote
);
14616 webkit_dom_element_set_attribute (
14617 wrapped_paragraph
, "data-user-wrapped", "", NULL
);
14619 if (quoted
&& !html_mode
)
14620 e_editor_dom_quote_plain_text_element_after_wrapping (
14621 editor_page
, wrapped_paragraph
, citation_level
);
14623 block
= next_block
;
14627 e_editor_dom_selection_get_coordinates (editor_page
,
14628 &ev
->after
.start
.x
,
14629 &ev
->after
.start
.y
,
14632 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
14635 e_editor_dom_selection_restore (editor_page
);
14637 e_editor_dom_force_spell_check_in_viewport (editor_page
);
14639 e_editor_page_emit_content_changed (editor_page
);
14643 e_editor_dom_wrap_paragraphs_in_document (EEditorPage
*editor_page
)
14645 WebKitDOMDocument
*document
;
14646 WebKitDOMNodeList
*list
= NULL
;
14649 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
14651 document
= e_editor_page_get_document (editor_page
);
14652 list
= webkit_dom_document_query_selector_all (
14653 document
, "[data-evo-paragraph]:not(#-x-evo-input-start)", NULL
);
14655 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
14656 gint word_wrap_length
, quote
, citation_level
;
14657 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
14659 citation_level
= e_editor_dom_get_citation_level (node
);
14660 quote
= citation_level
? citation_level
* 2 : 0;
14661 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14663 if (node_is_list (node
)) {
14664 WebKitDOMNode
*item
= webkit_dom_node_get_first_child (node
);
14666 while (item
&& WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
14667 e_editor_dom_wrap_paragraph_length (
14668 editor_page
, WEBKIT_DOM_ELEMENT (item
), word_wrap_length
- quote
);
14669 item
= webkit_dom_node_get_next_sibling (item
);
14672 e_editor_dom_wrap_paragraph_length (
14673 editor_page
, WEBKIT_DOM_ELEMENT (node
), word_wrap_length
- quote
);
14676 g_clear_object (&list
);
14680 e_editor_dom_wrap_paragraph (EEditorPage
*editor_page
,
14681 WebKitDOMElement
*paragraph
)
14683 gint indentation_level
, citation_level
, quote
;
14684 gint word_wrap_length
, final_width
, offset
= 0;
14686 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
14687 g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph
), NULL
);
14689 indentation_level
= get_indentation_level (paragraph
);
14690 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (paragraph
));
14692 if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph
))) {
14693 gint list_level
= get_list_level (WEBKIT_DOM_NODE (paragraph
));
14694 indentation_level
= 0;
14696 if (list_level
> 0)
14697 offset
= list_level
* -SPACES_PER_LIST_LEVEL
;
14699 offset
= -SPACES_PER_LIST_LEVEL
;
14702 quote
= citation_level
? citation_level
* 2 : 0;
14704 word_wrap_length
= e_editor_page_get_word_wrap_length (editor_page
);
14705 final_width
= word_wrap_length
- quote
+ offset
;
14706 final_width
-= SPACES_PER_INDENTATION
* indentation_level
;
14708 return e_editor_dom_wrap_paragraph_length (
14709 editor_page
, WEBKIT_DOM_ELEMENT (paragraph
), final_width
);
14713 get_has_style (EEditorPage
*editor_page
,
14714 const gchar
*style_tag
)
14716 WebKitDOMNode
*node
;
14717 WebKitDOMElement
*element
;
14718 WebKitDOMRange
*range
= NULL
;
14722 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
14724 range
= e_editor_dom_get_current_range (editor_page
);
14728 node
= webkit_dom_range_get_start_container (range
, NULL
);
14729 if (WEBKIT_DOM_IS_ELEMENT (node
))
14730 element
= WEBKIT_DOM_ELEMENT (node
);
14732 element
= webkit_dom_node_get_parent_element (node
);
14733 g_clear_object (&range
);
14735 tag_len
= strlen (style_tag
);
14737 while (!result
&& element
) {
14738 gchar
*element_tag
;
14739 gboolean accept_citation
= FALSE
;
14741 element_tag
= webkit_dom_element_get_tag_name (element
);
14743 if (g_ascii_strncasecmp (style_tag
, "citation", 8) == 0) {
14744 accept_citation
= TRUE
;
14745 result
= WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element
);
14746 if (element_has_class (element
, "-x-evo-indented"))
14749 result
= ((tag_len
== strlen (element_tag
)) &&
14750 (g_ascii_strncasecmp (element_tag
, style_tag
, tag_len
) == 0));
14753 /* Special case: <blockquote type=cite> marks quotation, while
14754 * just <blockquote> is used for indentation. If the <blockquote>
14755 * has type=cite, then ignore it unless style_tag is "citation" */
14756 if (result
&& WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element
)) {
14757 if (webkit_dom_element_has_attribute (element
, "type")) {
14758 gchar
*type
= webkit_dom_element_get_attribute (element
, "type");
14759 if (!accept_citation
&& (type
&& g_ascii_strncasecmp (type
, "cite", 4) == 0)) {
14764 if (accept_citation
)
14769 g_free (element_tag
);
14774 element
= webkit_dom_node_get_parent_element (
14775 WEBKIT_DOM_NODE (element
));
14781 typedef gboolean (*IsRightFormatNodeFunc
) (WebKitDOMElement
*element
);
14784 dom_selection_is_font_format (EEditorPage
*editor_page
,
14785 IsRightFormatNodeFunc func
,
14786 gboolean
*previous_value
)
14788 WebKitDOMDocument
*document
;
14789 WebKitDOMDOMWindow
*dom_window
= NULL
;
14790 WebKitDOMDOMSelection
*dom_selection
= NULL
;
14791 WebKitDOMNode
*start
, *end
, *sibling
;
14792 WebKitDOMRange
*range
= NULL
;
14793 gboolean ret_val
= FALSE
;
14795 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
14797 if (!e_editor_page_get_html_mode (editor_page
))
14800 document
= e_editor_page_get_document (editor_page
);
14801 dom_window
= webkit_dom_document_get_default_view (document
);
14802 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
14803 g_clear_object (&dom_window
);
14805 if (!webkit_dom_dom_selection_get_range_count (dom_selection
))
14808 range
= webkit_dom_dom_selection_get_range_at (dom_selection
, 0, NULL
);
14812 if (webkit_dom_range_get_collapsed (range
, NULL
) && previous_value
) {
14813 WebKitDOMNode
*node
;
14814 gchar
* text_content
;
14816 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
14817 /* If we are changing the format of block we have to re-set the
14818 * format property, otherwise it will be turned off because of no
14819 * text in block. */
14820 text_content
= webkit_dom_node_get_text_content (node
);
14821 if (g_strcmp0 (text_content
, "") == 0) {
14822 g_free (text_content
);
14823 ret_val
= *previous_value
;
14826 g_free (text_content
);
14829 /* Range without start or end point is a wrong range. */
14830 start
= webkit_dom_range_get_start_container (range
, NULL
);
14831 end
= webkit_dom_range_get_end_container (range
, NULL
);
14832 if (!start
|| !end
)
14835 if (WEBKIT_DOM_IS_TEXT (start
))
14836 start
= webkit_dom_node_get_parent_node (start
);
14837 while (start
&& WEBKIT_DOM_IS_ELEMENT (start
) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start
)) {
14838 /* Find the start point's parent node with given formatting. */
14839 if (func (WEBKIT_DOM_ELEMENT (start
))) {
14843 start
= webkit_dom_node_get_parent_node (start
);
14846 /* Start point doesn't have the given formatting. */
14850 /* If the selection is collapsed, we can return early. */
14851 if (webkit_dom_range_get_collapsed (range
, NULL
))
14854 /* The selection is in the same node and that node is supposed to have
14855 * the same formatting (otherwise it is split up with formatting element. */
14856 if (webkit_dom_node_is_same_node (
14857 webkit_dom_range_get_start_container (range
, NULL
),
14858 webkit_dom_range_get_end_container (range
, NULL
)))
14863 if (WEBKIT_DOM_IS_TEXT (end
))
14864 end
= webkit_dom_node_get_parent_node (end
);
14865 while (end
&& WEBKIT_DOM_IS_ELEMENT (end
) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end
)) {
14866 /* Find the end point's parent node with given formatting. */
14867 if (func (WEBKIT_DOM_ELEMENT (end
))) {
14871 end
= webkit_dom_node_get_parent_node (end
);
14879 /* Now go between the end points and check the inner nodes for format validity. */
14881 while ((sibling
= webkit_dom_node_get_next_sibling (sibling
))) {
14882 if (webkit_dom_node_is_same_node (sibling
, end
)) {
14887 if (WEBKIT_DOM_IS_TEXT (sibling
))
14889 else if (func (WEBKIT_DOM_ELEMENT (sibling
)))
14891 else if (webkit_dom_node_get_first_child (sibling
)) {
14892 WebKitDOMNode
*first_child
;
14894 first_child
= webkit_dom_node_get_first_child (sibling
);
14895 if (!webkit_dom_node_get_next_sibling (first_child
))
14896 if (WEBKIT_DOM_IS_ELEMENT (first_child
) && func (WEBKIT_DOM_ELEMENT (first_child
)))
14907 while ((sibling
= webkit_dom_node_get_previous_sibling (sibling
))) {
14908 if (webkit_dom_node_is_same_node (sibling
, start
))
14911 if (WEBKIT_DOM_IS_TEXT (sibling
))
14913 else if (func (WEBKIT_DOM_ELEMENT (sibling
)))
14915 else if (webkit_dom_node_get_first_child (sibling
)) {
14916 WebKitDOMNode
*first_child
;
14918 first_child
= webkit_dom_node_get_first_child (sibling
);
14919 if (!webkit_dom_node_get_next_sibling (first_child
))
14920 if (WEBKIT_DOM_IS_ELEMENT (first_child
) && func (WEBKIT_DOM_ELEMENT (first_child
)))
14932 g_clear_object (&range
);
14933 g_clear_object (&dom_selection
);
14939 is_underline_element (WebKitDOMElement
*element
)
14941 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
14944 return element_has_tag (element
, "u");
14948 * e_html_editor_selection_is_underline:
14949 * @selection: an #EEditorSelection
14951 * Returns whether current selection or letter at current cursor position
14954 * Returns @TRUE when selection is underlined, @FALSE otherwise.
14957 e_editor_dom_selection_is_underline (EEditorPage
*editor_page
)
14959 gboolean is_underline
;
14961 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
14963 is_underline
= e_editor_page_get_underline (editor_page
);
14964 is_underline
= dom_selection_is_font_format (
14965 editor_page
, (IsRightFormatNodeFunc
) is_underline_element
, &is_underline
);
14967 return is_underline
;
14970 static WebKitDOMElement
*
14971 set_font_style (WebKitDOMDocument
*document
,
14972 const gchar
*element_name
,
14975 WebKitDOMElement
*element
;
14976 WebKitDOMNode
*parent
, *clone
= NULL
;
14978 element
= webkit_dom_document_get_element_by_id (document
, "-x-evo-selection-end-marker");
14979 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
14981 WebKitDOMNode
*node
;
14982 WebKitDOMElement
*el
;
14985 el
= webkit_dom_document_create_element (document
, element_name
, NULL
);
14986 webkit_dom_html_element_set_inner_text (
14987 WEBKIT_DOM_HTML_ELEMENT (el
), UNICODE_ZERO_WIDTH_SPACE
, NULL
);
14989 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
14990 webkit_dom_node_append_child (
14991 WEBKIT_DOM_NODE (el
), node
, NULL
);
14992 name
= webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (parent
));
14993 if (g_ascii_strcasecmp (name
, element_name
) == 0 && g_ascii_strcasecmp (name
, "font") != 0)
14994 webkit_dom_node_insert_before (
14995 webkit_dom_node_get_parent_node (parent
),
14996 WEBKIT_DOM_NODE (el
),
14997 webkit_dom_node_get_next_sibling (parent
),
15000 webkit_dom_node_insert_before (
15002 WEBKIT_DOM_NODE (el
),
15003 WEBKIT_DOM_NODE (element
),
15007 webkit_dom_node_append_child (
15008 WEBKIT_DOM_NODE (el
), WEBKIT_DOM_NODE (element
), NULL
);
15012 gboolean no_sibling
;
15013 WebKitDOMNode
*node
, *sibling
;
15015 node
= webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element
));
15017 /* Turning the formatting in the middle of element. */
15018 sibling
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
));
15019 no_sibling
= sibling
&&
15020 !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) &&
15021 !webkit_dom_node_get_next_sibling (sibling
);
15024 gboolean do_clone
= TRUE
;
15025 gchar
*text_content
= NULL
;
15026 WebKitDOMNode
*child
;
15028 if ((text_content
= webkit_dom_node_get_text_content (parent
)) &&
15029 (g_strcmp0 (text_content
, UNICODE_ZERO_WIDTH_SPACE
) == 0))
15032 g_free (text_content
);
15035 clone
= webkit_dom_node_clone_node_with_error (
15036 WEBKIT_DOM_NODE (parent
), FALSE
, NULL
);
15038 while ((child
= webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element
))))
15039 webkit_dom_node_insert_before (
15042 webkit_dom_node_get_first_child (clone
),
15045 webkit_dom_node_insert_before (
15046 webkit_dom_node_get_parent_node (parent
),
15048 webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent
)),
15053 webkit_dom_node_insert_before (
15054 webkit_dom_node_get_parent_node (parent
),
15055 WEBKIT_DOM_NODE (element
),
15056 webkit_dom_node_get_next_sibling (parent
),
15058 webkit_dom_node_insert_before (
15059 webkit_dom_node_get_parent_node (parent
),
15061 webkit_dom_node_get_next_sibling (parent
),
15064 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling
) && !no_sibling
) {
15065 webkit_dom_node_insert_before (
15066 webkit_dom_node_get_parent_node (parent
),
15068 webkit_dom_node_get_next_sibling (parent
),
15072 if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent
))) {
15073 WebKitDOMNode
*first_child
;
15075 if ((first_child
= webkit_dom_node_get_first_child (parent
))) {
15076 gchar
*text_content
= NULL
;
15078 text_content
= webkit_dom_node_get_text_content (first_child
);
15080 if (g_strcmp0 (text_content
, UNICODE_ZERO_WIDTH_SPACE
) != 0)
15081 webkit_dom_element_insert_adjacent_text (
15082 WEBKIT_DOM_ELEMENT (parent
),
15084 UNICODE_ZERO_WIDTH_SPACE
,
15087 g_free (text_content
);
15090 remove_node_if_empty (parent
);
15091 remove_node_if_empty (clone
);
15099 selection_set_font_style (EEditorPage
*editor_page
,
15100 EContentEditorCommand command
,
15103 EEditorHistoryEvent
*ev
= NULL
;
15104 EEditorUndoRedoManager
*manager
;
15106 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15108 e_editor_dom_selection_save (editor_page
);
15110 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
15111 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
15112 ev
= g_new0 (EEditorHistoryEvent
, 1);
15113 if (command
== E_CONTENT_EDITOR_COMMAND_BOLD
)
15114 ev
->type
= HISTORY_BOLD
;
15115 else if (command
== E_CONTENT_EDITOR_COMMAND_ITALIC
)
15116 ev
->type
= HISTORY_ITALIC
;
15117 else if (command
== E_CONTENT_EDITOR_COMMAND_UNDERLINE
)
15118 ev
->type
= HISTORY_UNDERLINE
;
15119 else if (command
== E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
)
15120 ev
->type
= HISTORY_STRIKETHROUGH
;
15122 e_editor_dom_selection_get_coordinates (editor_page
,
15123 &ev
->before
.start
.x
,
15124 &ev
->before
.start
.y
,
15126 &ev
->before
.end
.y
);
15128 ev
->data
.style
.from
= !value
;
15129 ev
->data
.style
.to
= value
;
15132 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
15133 const gchar
*element_name
= NULL
;
15135 if (command
== E_CONTENT_EDITOR_COMMAND_BOLD
)
15136 element_name
= "b";
15137 else if (command
== E_CONTENT_EDITOR_COMMAND_ITALIC
)
15138 element_name
= "i";
15139 else if (command
== E_CONTENT_EDITOR_COMMAND_UNDERLINE
)
15140 element_name
= "u";
15141 else if (command
== E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
)
15142 element_name
= "strike";
15145 set_font_style (e_editor_page_get_document (editor_page
), element_name
, value
);
15146 e_editor_dom_selection_restore (editor_page
);
15150 e_editor_dom_selection_restore (editor_page
);
15152 e_editor_dom_exec_command (editor_page
, command
, NULL
);
15155 e_editor_dom_selection_get_coordinates (editor_page
,
15156 &ev
->after
.start
.x
,
15157 &ev
->after
.start
.y
,
15160 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
15163 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
15167 * e_html_editor_selection_set_underline:
15168 * @selection: an #EEditorSelection
15169 * @underline: @TRUE to enable underline, @FALSE to disable
15171 * Toggles underline formatting of current selection or letter at current
15172 * cursor position, depending on whether @underline is @TRUE or @FALSE.
15175 e_editor_dom_selection_set_underline (EEditorPage
*editor_page
,
15176 gboolean underline
)
15178 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15180 if (e_editor_dom_selection_is_underline (editor_page
) == underline
)
15183 selection_set_font_style (
15184 editor_page
, E_CONTENT_EDITOR_COMMAND_UNDERLINE
, underline
);
15188 is_subscript_element (WebKitDOMElement
*element
)
15190 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15193 return element_has_tag (element
, "sub");
15197 * e_html_editor_selection_is_subscript:
15198 * @selection: an #EEditorSelection
15200 * Returns whether current selection or letter at current cursor position
15203 * Returns @TRUE when selection is in subscript, @FALSE otherwise.
15206 e_editor_dom_selection_is_subscript (EEditorPage
*editor_page
)
15208 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15210 return dom_selection_is_font_format (
15211 editor_page
, (IsRightFormatNodeFunc
) is_subscript_element
, NULL
);
15215 * e_html_editor_selection_set_subscript:
15216 * @selection: an #EEditorSelection
15217 * @subscript: @TRUE to enable subscript, @FALSE to disable
15219 * Toggles subscript of current selection or letter at current cursor position,
15220 * depending on whether @subscript is @TRUE or @FALSE.
15223 e_editor_dom_selection_set_subscript (EEditorPage
*editor_page
,
15224 gboolean subscript
)
15226 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15228 if (e_editor_dom_selection_is_subscript (editor_page
) == subscript
)
15231 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_SUBSCRIPT
, NULL
);
15235 is_superscript_element (WebKitDOMElement
*element
)
15237 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15240 return element_has_tag (element
, "sup");
15244 * e_html_editor_selection_is_superscript:
15245 * @selection: an #EEditorSelection
15247 * Returns whether current selection or letter at current cursor position
15248 * is in superscript.
15250 * Returns @TRUE when selection is in superscript, @FALSE otherwise.
15253 e_editor_dom_selection_is_superscript (EEditorPage
*editor_page
)
15255 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15257 return dom_selection_is_font_format (
15258 editor_page
, (IsRightFormatNodeFunc
) is_superscript_element
, NULL
);
15262 * e_html_editor_selection_set_superscript:
15263 * @selection: an #EEditorSelection
15264 * @superscript: @TRUE to enable superscript, @FALSE to disable
15266 * Toggles superscript of current selection or letter at current cursor position,
15267 * depending on whether @superscript is @TRUE or @FALSE.
15270 e_editor_dom_selection_set_superscript (EEditorPage
*editor_page
,
15271 gboolean superscript
)
15273 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15275 if (e_editor_dom_selection_is_superscript (editor_page
) == superscript
)
15278 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT
, NULL
);
15282 is_strikethrough_element (WebKitDOMElement
*element
)
15284 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15287 return element_has_tag (element
, "strike");
15291 * e_html_editor_selection_is_strikethrough:
15292 * @selection: an #EEditorSelection
15294 * Returns whether current selection or letter at current cursor position
15295 * is striked through.
15297 * Returns @TRUE when selection is striked through, @FALSE otherwise.
15300 e_editor_dom_selection_is_strikethrough (EEditorPage
*editor_page
)
15302 gboolean is_strikethrough
;
15304 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15306 is_strikethrough
= e_editor_page_get_strikethrough (editor_page
);
15307 is_strikethrough
= dom_selection_is_font_format (
15308 editor_page
, (IsRightFormatNodeFunc
) is_strikethrough_element
, &is_strikethrough
);
15310 return is_strikethrough
;
15314 * e_html_editor_selection_set_strikethrough:
15315 * @selection: an #EEditorSelection
15316 * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
15318 * Toggles strike through formatting of current selection or letter at current
15319 * cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
15322 e_editor_dom_selection_set_strikethrough (EEditorPage
*editor_page
,
15323 gboolean strikethrough
)
15325 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15327 if (e_editor_dom_selection_is_strikethrough (editor_page
) == strikethrough
)
15330 selection_set_font_style (
15331 editor_page
, E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH
, strikethrough
);
15335 is_monospace_element (WebKitDOMElement
*element
)
15338 gboolean ret_val
= FALSE
;
15343 if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element
))
15346 value
= webkit_dom_element_get_attribute (element
, "face");
15347 if (value
&& g_strcmp0 (value
, "monospace") == 0)
15356 * e_html_editor_selection_is_monospaced:
15357 * @selection: an #EEditorSelection
15359 * Returns whether current selection or letter at current cursor position
15362 * Returns @TRUE when selection is monospaced, @FALSE otherwise.
15365 e_editor_dom_selection_is_monospace (EEditorPage
*editor_page
)
15367 gboolean is_monospace
;
15369 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15371 is_monospace
= e_editor_page_get_monospace (editor_page
);
15372 is_monospace
= dom_selection_is_font_format (
15373 editor_page
, (IsRightFormatNodeFunc
) is_monospace_element
, &is_monospace
);
15375 return is_monospace
;
15379 monospace_selection (EEditorPage
*editor_page
,
15380 WebKitDOMElement
*monospace_element
)
15382 WebKitDOMDocument
*document
;
15383 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
15384 WebKitDOMNode
*sibling
, *node
, *monospace
, *block
;
15385 WebKitDOMNodeList
*list
= NULL
;
15386 gboolean selection_end
= FALSE
;
15387 gboolean first
= TRUE
;
15390 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15392 document
= e_editor_page_get_document (editor_page
);
15393 e_editor_dom_selection_save (editor_page
);
15395 selection_start_marker
= webkit_dom_document_get_element_by_id (
15396 document
, "-x-evo-selection-start-marker");
15397 selection_end_marker
= webkit_dom_document_get_element_by_id (
15398 document
, "-x-evo-selection-end-marker");
15400 block
= WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker
)));
15402 monospace
= WEBKIT_DOM_NODE (monospace_element
);
15403 node
= WEBKIT_DOM_NODE (selection_start_marker
);
15404 /* Go through first block in selection. */
15405 while (block
&& node
&& !webkit_dom_node_is_same_node (block
, node
)) {
15406 if (webkit_dom_node_get_next_sibling (node
)) {
15407 /* Prepare the monospaced element. */
15408 monospace
= webkit_dom_node_insert_before (
15409 webkit_dom_node_get_parent_node (node
),
15410 first
? monospace
: webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15411 first
? node
: webkit_dom_node_get_next_sibling (node
),
15416 /* Move the nodes into monospaced element. */
15417 while (((sibling
= webkit_dom_node_get_next_sibling (monospace
)))) {
15418 webkit_dom_node_append_child (monospace
, sibling
, NULL
);
15419 if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker
), sibling
)) {
15420 selection_end
= TRUE
;
15425 node
= webkit_dom_node_get_parent_node (monospace
);
15429 /* Just one block was selected. */
15433 /* Middle blocks (blocks not containing the end of the selection. */
15434 block
= webkit_dom_node_get_next_sibling (block
);
15435 while (block
&& !selection_end
) {
15436 WebKitDOMNode
*next_block
;
15438 selection_end
= webkit_dom_node_contains (
15439 block
, WEBKIT_DOM_NODE (selection_end_marker
));
15444 next_block
= webkit_dom_node_get_next_sibling (block
);
15446 monospace
= webkit_dom_node_insert_before (
15448 webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15449 webkit_dom_node_get_first_child (block
),
15452 while (((sibling
= webkit_dom_node_get_next_sibling (monospace
))))
15453 webkit_dom_node_append_child (monospace
, sibling
, NULL
);
15455 block
= next_block
;
15458 /* Block containing the end of selection. */
15459 node
= WEBKIT_DOM_NODE (selection_end_marker
);
15460 while (block
&& node
&& !webkit_dom_node_is_same_node (block
, node
)) {
15461 monospace
= webkit_dom_node_insert_before (
15462 webkit_dom_node_get_parent_node (node
),
15463 webkit_dom_node_clone_node_with_error (monospace
, FALSE
, NULL
),
15464 webkit_dom_node_get_next_sibling (node
),
15467 while (((sibling
= webkit_dom_node_get_previous_sibling (monospace
)))) {
15468 webkit_dom_node_insert_before (
15471 webkit_dom_node_get_first_child (monospace
),
15475 node
= webkit_dom_node_get_parent_node (monospace
);
15478 /* Merge all the monospace elements inside other monospace elements. */
15479 list
= webkit_dom_document_query_selector_all (
15480 document
, "font[face=monospace] > font[face=monospace]", NULL
);
15481 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
15482 WebKitDOMNode
*item
;
15483 WebKitDOMNode
*child
;
15485 item
= webkit_dom_node_list_item (list
, ii
);
15486 while ((child
= webkit_dom_node_get_first_child (item
))) {
15487 webkit_dom_node_insert_before (
15488 webkit_dom_node_get_parent_node (item
),
15493 remove_node (item
);
15495 g_clear_object (&list
);
15497 /* Merge all the adjacent monospace elements. */
15498 list
= webkit_dom_document_query_selector_all (
15499 document
, "font[face=monospace] + font[face=monospace]", NULL
);
15500 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
15501 WebKitDOMNode
*item
;
15502 WebKitDOMNode
*child
;
15504 item
= webkit_dom_node_list_item (list
, ii
);
15505 /* The + CSS selector will return some false positives as it doesn't
15506 * take text between elements into account so it will return this:
15507 * <font face="monospace">xx</font>yy<font face="monospace">zz</font>
15508 * as valid, but it isn't so we have to check if previous node
15509 * is indeed element or not. */
15510 if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item
))) {
15511 while ((child
= webkit_dom_node_get_first_child (item
))) {
15512 webkit_dom_node_append_child (
15513 webkit_dom_node_get_previous_sibling (item
), child
, NULL
);
15515 remove_node (item
);
15518 g_clear_object (&list
);
15520 e_editor_dom_selection_restore (editor_page
);
15524 unmonospace_selection (EEditorPage
*editor_page
)
15526 WebKitDOMDocument
*document
;
15527 WebKitDOMElement
*selection_start_marker
;
15528 WebKitDOMElement
*selection_end_marker
;
15529 WebKitDOMElement
*selection_start_clone
;
15530 WebKitDOMElement
*selection_end_clone
;
15531 WebKitDOMNode
*sibling
, *node
;
15532 WebKitDOMNode
*block
, *clone
, *monospace
;
15533 gboolean selection_end
= FALSE
;
15535 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15537 document
= e_editor_page_get_document (editor_page
);
15538 e_editor_dom_selection_save (editor_page
);
15540 selection_start_marker
= webkit_dom_document_get_element_by_id (
15541 document
, "-x-evo-selection-start-marker");
15542 selection_end_marker
= webkit_dom_document_get_element_by_id (
15543 document
, "-x-evo-selection-end-marker");
15545 block
= WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker
)));
15547 node
= WEBKIT_DOM_NODE (selection_start_marker
);
15548 monospace
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker
));
15549 while (monospace
&& !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace
)))
15550 monospace
= webkit_dom_node_get_parent_node (monospace
);
15552 /* No monospaced element was found as a parent of selection start node. */
15556 /* Make a clone of current monospaced element. */
15557 clone
= webkit_dom_node_clone_node_with_error (monospace
, TRUE
, NULL
);
15560 /* Remove all the nodes that are after the selection start point as they
15561 * will be in the cloned node. */
15562 while (monospace
&& node
&& !webkit_dom_node_is_same_node (monospace
, node
)) {
15563 WebKitDOMNode
*tmp
;
15564 while (((sibling
= webkit_dom_node_get_next_sibling (node
))))
15565 remove_node (sibling
);
15567 tmp
= webkit_dom_node_get_parent_node (node
);
15568 if (webkit_dom_node_get_next_sibling (node
))
15569 remove_node (node
);
15573 selection_start_clone
= webkit_dom_element_query_selector (
15574 WEBKIT_DOM_ELEMENT (clone
), "#-x-evo-selection-start-marker", NULL
);
15575 selection_end_clone
= webkit_dom_element_query_selector (
15576 WEBKIT_DOM_ELEMENT (clone
), "#-x-evo-selection-end-marker", NULL
);
15578 /* No selection start node in the block where it is supposed to be, return. */
15579 if (!selection_start_clone
)
15582 /* Remove all the nodes until we hit the selection start point as these
15583 * nodes will stay monospaced and they are already in original element. */
15584 node
= webkit_dom_node_get_first_child (clone
);
15586 WebKitDOMNode
*next_sibling
;
15588 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15589 if (webkit_dom_node_get_first_child (node
)) {
15590 if (webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_start_clone
))) {
15591 node
= webkit_dom_node_get_first_child (node
);
15594 remove_node (node
);
15595 } else if (webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_start_clone
)))
15598 remove_node (node
);
15600 node
= next_sibling
;
15603 /* Insert the clone into the tree. Do it after the previous clean up. If
15604 * we would do it the other way the line would contain duplicated text nodes
15605 * and the block would be expading and shrinking while we would modify it. */
15606 webkit_dom_node_insert_before (
15607 webkit_dom_node_get_parent_node (monospace
),
15609 webkit_dom_node_get_next_sibling (monospace
),
15612 /* Move selection start point the right place. */
15613 remove_node (WEBKIT_DOM_NODE (selection_start_marker
));
15614 webkit_dom_node_insert_before (
15615 webkit_dom_node_get_parent_node (clone
),
15616 WEBKIT_DOM_NODE (selection_start_clone
),
15620 /* Move all the nodes the are supposed to lose the monospace formatting
15621 * out of monospaced element. */
15622 node
= webkit_dom_node_get_first_child (clone
);
15624 WebKitDOMNode
*next_sibling
;
15626 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15627 if (webkit_dom_node_get_first_child (node
)) {
15628 if (selection_end_clone
&&
15629 webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_end_clone
))) {
15630 node
= webkit_dom_node_get_first_child (node
);
15633 webkit_dom_node_insert_before (
15634 webkit_dom_node_get_parent_node (clone
),
15638 } else if (selection_end_clone
&&
15639 webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_end_clone
))) {
15640 selection_end
= TRUE
;
15641 webkit_dom_node_insert_before (
15642 webkit_dom_node_get_parent_node (clone
),
15648 webkit_dom_node_insert_before (
15649 webkit_dom_node_get_parent_node (clone
),
15654 node
= next_sibling
;
15657 remove_node_if_empty (clone
);
15658 remove_node_if_empty (monospace
);
15660 /* Just one block was selected and we hit the selection end point. */
15664 /* Middle blocks */
15665 block
= webkit_dom_node_get_next_sibling (block
);
15666 while (block
&& !selection_end
) {
15667 WebKitDOMNode
*next_block
, *child
, *parent
;
15668 WebKitDOMElement
*monospace_element
;
15670 selection_end
= webkit_dom_node_contains (
15671 block
, WEBKIT_DOM_NODE (selection_end_marker
));
15676 next_block
= webkit_dom_node_get_next_sibling (block
);
15678 /* Find the monospaced element and move all the nodes from it and
15679 * finally remove it. */
15680 monospace_element
= webkit_dom_element_query_selector (
15681 WEBKIT_DOM_ELEMENT (block
), "font[face=monospace]", NULL
);
15682 if (!monospace_element
)
15685 parent
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospace_element
));
15686 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospace_element
)))) {
15687 webkit_dom_node_insert_before (
15688 parent
, child
, WEBKIT_DOM_NODE (monospace_element
), NULL
);
15691 remove_node (WEBKIT_DOM_NODE (monospace_element
));
15693 block
= next_block
;
15697 node
= WEBKIT_DOM_NODE (selection_end_marker
);
15698 monospace
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker
));
15699 while (monospace
&& !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace
)))
15700 monospace
= webkit_dom_node_get_parent_node (monospace
);
15702 /* No monospaced element was found as a parent of selection end node. */
15706 clone
= WEBKIT_DOM_NODE (monospace
);
15707 node
= webkit_dom_node_get_first_child (clone
);
15708 /* Move all the nodes that are supposed to lose the monospaced formatting
15709 * out of the monospaced element. */
15711 WebKitDOMNode
*next_sibling
;
15713 next_sibling
= webkit_dom_node_get_next_sibling (node
);
15714 if (webkit_dom_node_get_first_child (node
)) {
15715 if (webkit_dom_node_contains (node
, WEBKIT_DOM_NODE (selection_end_marker
))) {
15716 node
= webkit_dom_node_get_first_child (node
);
15719 webkit_dom_node_insert_before (
15720 webkit_dom_node_get_parent_node (clone
),
15724 } else if (webkit_dom_node_is_same_node (node
, WEBKIT_DOM_NODE (selection_end_marker
))) {
15725 selection_end
= TRUE
;
15726 webkit_dom_node_insert_before (
15727 webkit_dom_node_get_parent_node (clone
),
15733 webkit_dom_node_insert_before (
15734 webkit_dom_node_get_parent_node (clone
),
15740 node
= next_sibling
;
15743 remove_node_if_empty (clone
);
15745 e_editor_dom_selection_restore (editor_page
);
15749 * e_html_editor_selection_set_monospaced:
15750 * @selection: an #EEditorSelection
15751 * @monospaced: @TRUE to enable monospaced, @FALSE to disable
15753 * Toggles monospaced formatting of current selection or letter at current cursor
15754 * position, depending on whether @monospaced is @TRUE or @FALSE.
15757 e_editor_dom_selection_set_monospace (EEditorPage
*editor_page
,
15760 WebKitDOMDocument
*document
;
15761 WebKitDOMRange
*range
= NULL
;
15762 EEditorHistoryEvent
*ev
= NULL
;
15763 EEditorUndoRedoManager
*manager
;
15764 guint font_size
= 0;
15766 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15768 if ((e_editor_dom_selection_is_monospace (editor_page
) ? 1 : 0) == (value
? 1 : 0))
15771 document
= e_editor_page_get_document (editor_page
);
15772 range
= e_editor_dom_get_current_range (editor_page
);
15776 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
15777 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
15778 ev
= g_new0 (EEditorHistoryEvent
, 1);
15779 ev
->type
= HISTORY_MONOSPACE
;
15781 e_editor_dom_selection_get_coordinates (editor_page
,
15782 &ev
->before
.start
.x
,
15783 &ev
->before
.start
.y
,
15785 &ev
->before
.end
.y
);
15787 ev
->data
.style
.from
= !value
;
15788 ev
->data
.style
.to
= value
;
15791 font_size
= e_editor_page_get_font_size (editor_page
);
15792 if (font_size
== 0)
15793 font_size
= E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
15796 WebKitDOMElement
*monospace
;
15798 monospace
= webkit_dom_document_create_element (
15799 document
, "font", NULL
);
15800 webkit_dom_element_set_attribute (
15801 monospace
, "face", "monospace", NULL
);
15802 if (font_size
!= 0) {
15803 gchar
*font_size_str
;
15805 font_size_str
= g_strdup_printf ("%d", font_size
);
15806 webkit_dom_element_set_attribute (
15807 monospace
, "size", font_size_str
, NULL
);
15808 g_free (font_size_str
);
15811 if (!webkit_dom_range_get_collapsed (range
, NULL
))
15812 monospace_selection (editor_page
, monospace
);
15814 /* https://bugs.webkit.org/show_bug.cgi?id=15256 */
15815 webkit_dom_element_set_inner_html (
15817 UNICODE_ZERO_WIDTH_SPACE
,
15819 webkit_dom_range_insert_node (
15820 range
, WEBKIT_DOM_NODE (monospace
), NULL
);
15822 e_editor_dom_move_caret_into_element (editor_page
, monospace
, FALSE
);
15825 gboolean is_bold
= FALSE
, is_italic
= FALSE
;
15826 gboolean is_underline
= FALSE
, is_strikethrough
= FALSE
;
15827 WebKitDOMElement
*tt_element
;
15828 WebKitDOMNode
*node
;
15830 node
= webkit_dom_range_get_end_container (range
, NULL
);
15831 if (WEBKIT_DOM_IS_ELEMENT (node
) &&
15832 is_monospace_element (WEBKIT_DOM_ELEMENT (node
))) {
15833 tt_element
= WEBKIT_DOM_ELEMENT (node
);
15835 tt_element
= dom_node_find_parent_element (node
, "FONT");
15837 if (!is_monospace_element (tt_element
)) {
15838 g_clear_object (&range
);
15844 /* Save current formatting */
15845 is_bold
= e_editor_page_get_bold (editor_page
);
15846 is_italic
= e_editor_page_get_italic (editor_page
);
15847 is_underline
= e_editor_page_get_underline (editor_page
);
15848 is_strikethrough
= e_editor_page_get_strikethrough (editor_page
);
15850 if (!e_editor_dom_selection_is_collapsed (editor_page
))
15851 unmonospace_selection (editor_page
);
15853 e_editor_dom_selection_save (editor_page
);
15854 set_font_style (document
, "", FALSE
);
15855 e_editor_dom_selection_restore (editor_page
);
15858 /* Re-set formatting */
15860 e_editor_dom_selection_set_bold (editor_page
, TRUE
);
15862 e_editor_dom_selection_set_italic (editor_page
, TRUE
);
15864 e_editor_dom_selection_set_underline (editor_page
, TRUE
);
15865 if (is_strikethrough
)
15866 e_editor_dom_selection_set_strikethrough (editor_page
, TRUE
);
15869 e_editor_dom_selection_set_font_size (editor_page
, font_size
);
15873 e_editor_dom_selection_get_coordinates (editor_page
,
15874 &ev
->after
.start
.x
,
15875 &ev
->after
.start
.y
,
15878 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
15881 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
15883 g_clear_object (&range
);
15887 is_bold_element (WebKitDOMElement
*element
)
15889 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15892 if (element_has_tag (element
, "b"))
15895 /* Headings are bold by default */
15896 return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element
);
15900 * e_html_editor_selection_is_bold:
15901 * @selection: an #EEditorSelection
15903 * Returns whether current selection or letter at current cursor position
15906 * Returns @TRUE when selection is bold, @FALSE otherwise.
15909 e_editor_dom_selection_is_bold (EEditorPage
*editor_page
)
15913 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15915 is_bold
= e_editor_page_get_bold (editor_page
);
15916 is_bold
= dom_selection_is_font_format (
15917 editor_page
, (IsRightFormatNodeFunc
) is_bold_element
, &is_bold
);
15923 * e_html_editor_selection_set_bold:
15924 * @selection: an #EEditorSelection
15925 * @bold: @TRUE to enable bold, @FALSE to disable
15927 * Toggles bold formatting of current selection or letter at current cursor
15928 * position, depending on whether @bold is @TRUE or @FALSE.
15931 e_editor_dom_selection_set_bold (EEditorPage
*editor_page
,
15934 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15936 if (e_editor_dom_selection_is_bold (editor_page
) == bold
)
15939 selection_set_font_style (
15940 editor_page
, E_CONTENT_EDITOR_COMMAND_BOLD
, bold
);
15942 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
15946 is_italic_element (WebKitDOMElement
*element
)
15948 if (!element
|| !WEBKIT_DOM_IS_ELEMENT (element
))
15951 return element_has_tag (element
, "i") || element_has_tag (element
, "address");
15955 * e_html_editor_selection_is_italic:
15956 * @selection: an #EEditorSelection
15958 * Returns whether current selection or letter at current cursor position
15961 * Returns @TRUE when selection is italic, @FALSE otherwise.
15964 e_editor_dom_selection_is_italic (EEditorPage
*editor_page
)
15966 gboolean is_italic
;
15968 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
15970 is_italic
= e_editor_page_get_italic (editor_page
);
15971 is_italic
= dom_selection_is_font_format (
15972 editor_page
, (IsRightFormatNodeFunc
) is_italic_element
, &is_italic
);
15978 * e_html_editor_selection_set_italic:
15979 * @selection: an #EEditorSelection
15980 * @italic: @TRUE to enable italic, @FALSE to disable
15982 * Toggles italic formatting of current selection or letter at current cursor
15983 * position, depending on whether @italic is @TRUE or @FALSE.
15986 e_editor_dom_selection_set_italic (EEditorPage
*editor_page
,
15989 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
15991 if (e_editor_dom_selection_is_italic (editor_page
) == italic
)
15994 selection_set_font_style (
15995 editor_page
, E_CONTENT_EDITOR_COMMAND_ITALIC
, italic
);
15999 * e_html_editor_selection_is_indented:
16000 * @selection: an #EEditorSelection
16002 * Returns whether current paragraph is indented. This does not include
16003 * citations. To check, whether paragraph is a citation, use
16004 * e_html_editor_selection_is_citation().
16006 * Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
16009 e_editor_dom_selection_is_indented (EEditorPage
*editor_page
)
16011 WebKitDOMElement
*element
;
16012 WebKitDOMRange
*range
= NULL
;
16014 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16016 range
= e_editor_dom_get_current_range (editor_page
);
16020 if (webkit_dom_range_get_collapsed (range
, NULL
)) {
16021 element
= get_element_for_inspection (range
);
16022 g_clear_object (&range
);
16023 return element_has_class (element
, "-x-evo-indented");
16025 WebKitDOMNode
*node
;
16028 node
= webkit_dom_range_get_end_container (range
, NULL
);
16029 /* No selection or whole body selected */
16030 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
16033 element
= WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
16034 ret_val
= element_has_class (element
, "-x-evo-indented");
16038 node
= webkit_dom_range_get_start_container (range
, NULL
);
16039 /* No selection or whole body selected */
16040 if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node
))
16043 element
= WEBKIT_DOM_ELEMENT (get_parent_indented_block (node
));
16044 ret_val
= element_has_class (element
, "-x-evo-indented");
16046 g_clear_object (&range
);
16052 g_clear_object (&range
);
16058 * e_html_editor_selection_is_citation:
16059 * @selection: an #EEditorSelection
16061 * Returns whether current paragraph is a citation.
16063 * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
16066 e_editor_dom_selection_is_citation (EEditorPage
*editor_page
)
16068 WebKitDOMNode
*node
;
16069 WebKitDOMRange
*range
= NULL
;
16071 gchar
*value
, *text_content
;
16073 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16075 range
= e_editor_dom_get_current_range (editor_page
);
16079 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16080 g_clear_object (&range
);
16082 if (WEBKIT_DOM_IS_TEXT (node
))
16083 return get_has_style (editor_page
, "citation");
16085 text_content
= webkit_dom_node_get_text_content (node
);
16086 if (g_strcmp0 (text_content
, "") == 0) {
16087 g_free (text_content
);
16090 g_free (text_content
);
16092 value
= webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node
), "type");
16093 /* citation == <blockquote type='cite'> */
16094 if (value
&& strstr (value
, "cite"))
16097 ret_val
= get_has_style (editor_page
, "citation");
16104 get_font_property (EEditorPage
*editor_page
,
16105 const gchar
*font_property
)
16107 WebKitDOMRange
*range
= NULL
;
16108 WebKitDOMNode
*node
;
16109 WebKitDOMElement
*element
;
16112 range
= e_editor_dom_get_current_range (editor_page
);
16116 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16117 g_clear_object (&range
);
16118 element
= dom_node_find_parent_element (node
, "FONT");
16119 while (element
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (element
) &&
16120 !webkit_dom_element_has_attribute (element
, font_property
)) {
16121 element
= dom_node_find_parent_element (
16122 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)), "FONT");
16128 g_object_get (G_OBJECT (element
), font_property
, &value
, NULL
);
16134 * e_editor_dom_selection_get_font_size:
16135 * @selection: an #EEditorSelection
16137 * Returns point size of current selection or of letter at current cursor position.
16140 e_editor_dom_selection_get_font_size (EEditorPage
*editor_page
)
16144 gboolean increment
;
16146 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), 0);
16148 size
= get_font_property (editor_page
, "size");
16149 if (!(size
&& *size
)) {
16151 return E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
16154 /* We don't support increments, but when going through a content that
16155 * was not written in Evolution we can find it. In this case just report
16156 * the normal size. */
16157 /* FIXME: go through all parent and get the right value. */
16158 increment
= size
[0] == '+' || size
[0] == '-';
16159 size_int
= atoi (size
);
16162 if (increment
|| size_int
== 0)
16163 return E_CONTENT_EDITOR_FONT_SIZE_NORMAL
;
16169 * e_html_editor_selection_set_font_size:
16170 * @selection: an #EEditorSelection
16171 * @font_size: point size to apply
16173 * Sets font size of current selection or of letter at current cursor position
16177 e_editor_dom_selection_set_font_size (EEditorPage
*editor_page
,
16178 EContentEditorFontSize font_size
)
16180 WebKitDOMDocument
*document
;
16181 EEditorUndoRedoManager
*manager
;
16182 EEditorHistoryEvent
*ev
= NULL
;
16184 guint current_font_size
;
16186 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16188 document
= e_editor_page_get_document (editor_page
);
16189 current_font_size
= e_editor_dom_selection_get_font_size (editor_page
);
16190 if (current_font_size
== font_size
)
16193 e_editor_dom_selection_save (editor_page
);
16195 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
16196 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
16197 ev
= g_new0 (EEditorHistoryEvent
, 1);
16198 ev
->type
= HISTORY_FONT_SIZE
;
16200 e_editor_dom_selection_get_coordinates (editor_page
,
16201 &ev
->before
.start
.x
,
16202 &ev
->before
.start
.y
,
16204 &ev
->before
.end
.y
);
16206 ev
->data
.style
.from
= current_font_size
;
16207 ev
->data
.style
.to
= font_size
;
16210 size_str
= g_strdup_printf ("%d", font_size
);
16212 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
16213 WebKitDOMElement
*font
;
16215 font
= set_font_style (document
, "font", font_size
!= 3);
16217 webkit_dom_element_set_attribute (font
, "size", size_str
, NULL
);
16218 e_editor_dom_selection_restore (editor_page
);
16222 e_editor_dom_selection_restore (editor_page
);
16224 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FONT_SIZE
, size_str
);
16226 /* Text in <font size="3"></font> (size 3 is our default size) is a little
16227 * bit smaller than font outsize it. So move it outside of it. */
16228 if (font_size
== E_CONTENT_EDITOR_FONT_SIZE_NORMAL
) {
16229 WebKitDOMElement
*element
;
16231 element
= webkit_dom_document_query_selector (document
, "font[size=\"3\"]", NULL
);
16233 WebKitDOMNode
*child
;
16235 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element
))))
16236 webkit_dom_node_insert_before (
16237 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
16239 WEBKIT_DOM_NODE (element
),
16242 remove_node (WEBKIT_DOM_NODE (element
));
16250 e_editor_dom_selection_get_coordinates (editor_page
,
16251 &ev
->after
.start
.x
,
16252 &ev
->after
.start
.y
,
16256 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
16261 * e_html_editor_selection_set_font_name:
16262 * @selection: an #EEditorSelection
16263 * @font_name: a font name to apply
16265 * Sets font name of current selection or of letter at current cursor position
16269 e_editor_dom_selection_set_font_name (EEditorPage
*editor_page
,
16270 const gchar
*font_name
)
16272 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16274 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FONT_NAME
, font_name
);
16278 * e_html_editor_selection_get_font_name:
16279 * @selection: an #EEditorSelection
16281 * Returns name of font used in current selection or at letter at current cursor
16284 * Returns: A string with font name. [transfer-none]
16287 e_editor_dom_selection_get_font_name (EEditorPage
*editor_page
)
16289 WebKitDOMNode
*node
;
16290 WebKitDOMRange
*range
= NULL
;
16291 WebKitDOMCSSStyleDeclaration
*css
= NULL
;
16294 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
16296 range
= e_editor_dom_get_current_range (editor_page
);
16297 node
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
16298 g_clear_object (&range
);
16300 css
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node
));
16301 value
= webkit_dom_css_style_declaration_get_property_value (css
, "fontFamily");
16302 g_clear_object (&css
);
16308 * e_html_editor_selection_set_font_color:
16309 * @selection: an #EEditorSelection
16310 * @rgba: a #GdkRGBA
16312 * Sets font color of current selection or letter at current cursor position to
16313 * color defined in @rgba.
16316 e_editor_dom_selection_set_font_color (EEditorPage
*editor_page
,
16317 const gchar
*color
)
16319 EEditorUndoRedoManager
*manager
;
16320 EEditorHistoryEvent
*ev
= NULL
;
16322 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16324 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
16325 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
16326 ev
= g_new0 (EEditorHistoryEvent
, 1);
16327 ev
->type
= HISTORY_FONT_COLOR
;
16329 e_editor_dom_selection_get_coordinates (editor_page
,
16330 &ev
->before
.start
.x
,
16331 &ev
->before
.start
.y
,
16333 &ev
->before
.end
.y
);
16335 ev
->data
.string
.from
= g_strdup (e_editor_page_get_font_color (editor_page
));
16336 ev
->data
.string
.to
= g_strdup (color
);
16339 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_FORE_COLOR
, color
);
16342 ev
->after
.start
.x
= ev
->before
.start
.x
;
16343 ev
->after
.start
.y
= ev
->before
.start
.y
;
16344 ev
->after
.end
.x
= ev
->before
.end
.x
;
16345 ev
->after
.end
.y
= ev
->before
.end
.y
;
16347 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
16352 * e_html_editor_selection_get_font_color:
16353 * @selection: an #EEditorSelection
16354 * @rgba: a #GdkRGBA object to be set to current font color
16356 * Sets @rgba to contain color of current text selection or letter at current
16360 e_editor_dom_selection_get_font_color (EEditorPage
*editor_page
)
16362 WebKitDOMDocument
*document
;
16365 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
16367 document
= e_editor_page_get_document (editor_page
);
16368 color
= get_font_property (editor_page
, "color");
16369 if (!(color
&& *color
)) {
16370 WebKitDOMHTMLElement
*body
;
16372 body
= webkit_dom_document_get_body (document
);
16374 color
= webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body
));
16375 if (!(color
&& *color
)) {
16377 return g_strdup ("#000000");
16385 * e_html_editor_selection_get_block_format:
16386 * @selection: an #EEditorSelection
16388 * Returns block format of current paragraph.
16390 * Returns: #EContentEditorBlockFormat
16392 EContentEditorBlockFormat
16393 e_editor_dom_selection_get_block_format (EEditorPage
*editor_page
)
16395 WebKitDOMNode
*node
;
16396 WebKitDOMRange
*range
= NULL
;
16397 WebKitDOMElement
*element
;
16398 EContentEditorBlockFormat result
;
16400 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
);
16402 range
= e_editor_dom_get_current_range (editor_page
);
16404 return E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
;
16406 node
= webkit_dom_range_get_start_container (range
, NULL
);
16408 if ((element
= dom_node_find_parent_element (node
, "UL"))) {
16409 WebKitDOMElement
*tmp_element
;
16411 tmp_element
= dom_node_find_parent_element (node
, "OL");
16413 if (webkit_dom_node_contains (WEBKIT_DOM_NODE (tmp_element
), WEBKIT_DOM_NODE (element
)))
16414 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16416 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element
));
16418 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
16419 } else if ((element
= dom_node_find_parent_element (node
, "OL")) != NULL
) {
16420 WebKitDOMElement
*tmp_element
;
16422 tmp_element
= dom_node_find_parent_element (node
, "UL");
16424 if (webkit_dom_node_contains (WEBKIT_DOM_NODE (element
), WEBKIT_DOM_NODE (tmp_element
)))
16425 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16427 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element
));
16429 result
= dom_get_list_format_from_node (WEBKIT_DOM_NODE (element
));
16430 } else if (dom_node_find_parent_element (node
, "PRE")) {
16431 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
;
16432 } else if (dom_node_find_parent_element (node
, "ADDRESS")) {
16433 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS
;
16434 } else if (dom_node_find_parent_element (node
, "H1")) {
16435 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H1
;
16436 } else if (dom_node_find_parent_element (node
, "H2")) {
16437 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H2
;
16438 } else if (dom_node_find_parent_element (node
, "H3")) {
16439 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H3
;
16440 } else if (dom_node_find_parent_element (node
, "H4")) {
16441 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H4
;
16442 } else if (dom_node_find_parent_element (node
, "H5")) {
16443 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H5
;
16444 } else if (dom_node_find_parent_element (node
, "H6")) {
16445 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_H6
;
16447 /* Everything else is a paragraph (normal block) for us */
16448 result
= E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
;
16451 g_clear_object (&range
);
16457 change_leading_space_to_nbsp (WebKitDOMNode
*block
)
16459 WebKitDOMNode
*child
;
16461 if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block
))
16464 if ((child
= webkit_dom_node_get_first_child (block
)) &&
16465 WEBKIT_DOM_IS_CHARACTER_DATA (child
)) {
16468 data
= webkit_dom_character_data_substring_data (
16469 WEBKIT_DOM_CHARACTER_DATA (child
), 0, 1, NULL
);
16471 if (data
&& *data
== ' ')
16472 webkit_dom_character_data_replace_data (
16473 WEBKIT_DOM_CHARACTER_DATA (child
), 0, 1, UNICODE_NBSP
, NULL
);
16479 change_trailing_space_in_block_to_nbsp (WebKitDOMNode
*block
)
16481 WebKitDOMNode
*child
;
16483 if ((child
= webkit_dom_node_get_last_child (block
)) &&
16484 WEBKIT_DOM_IS_CHARACTER_DATA (child
)) {
16488 length
= webkit_dom_character_data_get_length (
16489 WEBKIT_DOM_CHARACTER_DATA (child
));
16491 tmp
= webkit_dom_character_data_substring_data (
16492 WEBKIT_DOM_CHARACTER_DATA (child
), length
- 1, 1, NULL
);
16493 if (tmp
&& *tmp
== ' ') {
16494 webkit_dom_character_data_replace_data (
16495 WEBKIT_DOM_CHARACTER_DATA (child
),
16506 change_space_before_selection_to_nbsp (WebKitDOMNode
*node
)
16508 WebKitDOMNode
*prev_sibling
;
16510 if ((prev_sibling
= webkit_dom_node_get_previous_sibling (node
))) {
16511 if (WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling
)) {
16515 length
= webkit_dom_character_data_get_length (
16516 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
));
16518 tmp
= webkit_dom_character_data_substring_data (
16519 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
), length
- 1, 1, NULL
);
16520 if (tmp
&& *tmp
== ' ') {
16521 webkit_dom_character_data_replace_data (
16522 WEBKIT_DOM_CHARACTER_DATA (prev_sibling
),
16534 process_block_to_block (EEditorPage
*editor_page
,
16535 EContentEditorBlockFormat format
,
16536 const gchar
*value
,
16537 WebKitDOMNode
*block
,
16538 WebKitDOMNode
*end_block
,
16539 WebKitDOMNode
*blockquote
,
16540 gboolean html_mode
)
16542 WebKitDOMDocument
*document
;
16543 WebKitDOMNode
*next_block
;
16544 gboolean after_selection_end
= FALSE
;
16546 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), FALSE
);
16548 document
= e_editor_page_get_document (editor_page
);
16550 while (!after_selection_end
&& block
) {
16551 gboolean quoted
= FALSE
;
16552 gboolean empty
= FALSE
;
16554 gint citation_level
= 0;
16555 WebKitDOMNode
*child
;
16556 WebKitDOMElement
*element
;
16558 if (e_editor_dom_node_is_citation_node (block
)) {
16561 next_block
= webkit_dom_node_get_next_sibling (block
);
16562 finished
= process_block_to_block (
16566 webkit_dom_node_get_first_child (block
),
16574 block
= next_block
;
16579 if (webkit_dom_element_query_selector (
16580 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
16582 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
16586 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
16588 after_selection_end
= webkit_dom_node_is_same_node (block
, end_block
);
16590 next_block
= webkit_dom_node_get_next_sibling (block
);
16592 if (node_is_list (block
)) {
16593 WebKitDOMNode
*item
;
16595 item
= webkit_dom_node_get_first_child (block
);
16596 while (item
&& !WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
))
16597 item
= webkit_dom_node_get_first_child (item
);
16599 if (item
&& do_format_change_list_to_block (editor_page
, format
, item
, value
))
16602 block
= next_block
;
16607 if (format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
)
16608 element
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
16610 element
= webkit_dom_document_create_element (
16611 document
, value
, NULL
);
16613 content
= webkit_dom_node_get_text_content (block
);
16615 empty
= !*content
|| (g_strcmp0 (content
, UNICODE_ZERO_WIDTH_SPACE
) == 0);
16618 change_leading_space_to_nbsp (block
);
16619 change_trailing_space_in_block_to_nbsp (block
);
16621 while ((child
= webkit_dom_node_get_first_child (block
))) {
16622 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
16625 webkit_dom_node_append_child (
16626 WEBKIT_DOM_NODE (element
), child
, NULL
);
16630 WebKitDOMElement
*br
;
16632 br
= webkit_dom_document_create_element (
16633 document
, "BR", NULL
);
16634 webkit_dom_node_append_child (
16635 WEBKIT_DOM_NODE (element
), WEBKIT_DOM_NODE (br
), NULL
);
16638 webkit_dom_node_insert_before (
16639 webkit_dom_node_get_parent_node (block
),
16640 WEBKIT_DOM_NODE (element
),
16644 remove_node (block
);
16646 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
16648 if (!next_block
&& !after_selection_end
&& citation_level
> 0) {
16649 next_block
= webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
));
16650 next_block
= webkit_dom_node_get_next_sibling (next_block
);
16653 block
= next_block
;
16655 if (!html_mode
&& format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
) {
16656 citation_level
= e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element
));
16658 if (citation_level
> 0) {
16659 gint quote
, word_wrap_length
;
16662 e_editor_page_get_word_wrap_length (editor_page
);
16663 quote
= citation_level
* 2;
16665 element
= e_editor_dom_wrap_paragraph_length (
16666 editor_page
, element
, word_wrap_length
- quote
);
16671 if (!html_mode
&& quoted
&& citation_level
> 0)
16672 e_editor_dom_quote_plain_text_element_after_wrapping (
16673 editor_page
, element
, citation_level
);
16676 return after_selection_end
;
16680 format_change_block_to_block (EEditorPage
*editor_page
,
16681 EContentEditorBlockFormat format
,
16682 const gchar
*value
)
16684 WebKitDOMDocument
*document
;
16685 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
16686 WebKitDOMNode
*block
, *end_block
, *blockquote
= NULL
;
16687 gboolean html_mode
= FALSE
;
16689 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16691 document
= e_editor_page_get_document (editor_page
);
16692 selection_start_marker
= webkit_dom_document_get_element_by_id (
16693 document
, "-x-evo-selection-start-marker");
16694 selection_end_marker
= webkit_dom_document_get_element_by_id (
16695 document
, "-x-evo-selection-end-marker");
16697 /* If the selection was not saved, move it into the first child of body */
16698 if (!selection_start_marker
|| !selection_end_marker
) {
16699 WebKitDOMHTMLElement
*body
;
16700 WebKitDOMNode
*child
;
16702 body
= webkit_dom_document_get_body (document
);
16703 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
16705 dom_add_selection_markers_into_element_start (
16707 WEBKIT_DOM_ELEMENT (child
),
16708 &selection_start_marker
,
16709 &selection_end_marker
);
16712 block
= e_editor_dom_get_parent_block_node_from_child (
16713 WEBKIT_DOM_NODE (selection_start_marker
));
16715 html_mode
= e_editor_page_get_html_mode (editor_page
);
16717 end_block
= e_editor_dom_get_parent_block_node_from_child (
16718 WEBKIT_DOM_NODE (selection_end_marker
));
16720 /* Process all blocks that are in the selection one by one */
16721 process_block_to_block (
16722 editor_page
, format
, value
, block
, end_block
, blockquote
, html_mode
);
16726 format_change_block_to_list (EEditorPage
*editor_page
,
16727 EContentEditorBlockFormat format
)
16729 WebKitDOMDocument
*document
;
16730 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *item
, *list
;
16731 WebKitDOMNode
*block
, *next_block
;
16732 gboolean after_selection_end
= FALSE
, in_quote
= FALSE
;
16733 gboolean html_mode
= e_editor_page_get_html_mode (editor_page
);
16735 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16737 document
= e_editor_page_get_document (editor_page
);
16738 selection_start_marker
= webkit_dom_document_get_element_by_id (
16739 document
, "-x-evo-selection-start-marker");
16740 selection_end_marker
= webkit_dom_document_get_element_by_id (
16741 document
, "-x-evo-selection-end-marker");
16743 /* If the selection was not saved, move it into the first child of body */
16744 if (!selection_start_marker
|| !selection_end_marker
) {
16745 WebKitDOMHTMLElement
*body
;
16746 WebKitDOMNode
*child
;
16748 body
= webkit_dom_document_get_body (document
);
16749 child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body
));
16751 dom_add_selection_markers_into_element_start (
16753 WEBKIT_DOM_ELEMENT (child
),
16754 &selection_start_marker
,
16755 &selection_end_marker
);
16758 block
= e_editor_dom_get_parent_block_node_from_child (
16759 WEBKIT_DOM_NODE (selection_start_marker
));
16761 list
= create_list_element (editor_page
, format
, 0, html_mode
);
16763 if (webkit_dom_element_query_selector (
16764 WEBKIT_DOM_ELEMENT (block
), "span.-x-evo-quoted", NULL
)) {
16765 WebKitDOMElement
*element
;
16766 WebKitDOMDOMWindow
*dom_window
= NULL
;
16767 WebKitDOMDOMSelection
*dom_selection
= NULL
;
16768 WebKitDOMRange
*range
= NULL
;
16772 dom_window
= webkit_dom_document_get_default_view (document
);
16773 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
16774 range
= webkit_dom_document_create_range (document
);
16776 webkit_dom_range_select_node (range
, block
, NULL
);
16777 webkit_dom_range_collapse (range
, TRUE
, NULL
);
16778 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
16779 webkit_dom_dom_selection_add_range (dom_selection
, range
);
16781 g_clear_object (&range
);
16782 g_clear_object (&dom_selection
);
16783 g_clear_object (&dom_window
);
16785 e_editor_dom_remove_input_event_listener_from_body (editor_page
);
16786 e_editor_page_block_selection_changed (editor_page
);
16788 e_editor_dom_exec_command (
16789 editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT
, NULL
);
16791 e_editor_dom_register_input_event_listener_on_body (editor_page
);
16792 e_editor_page_unblock_selection_changed (editor_page
);
16794 element
= webkit_dom_document_query_selector (
16795 document
, "body>br", NULL
);
16797 webkit_dom_node_replace_child (
16798 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element
)),
16799 WEBKIT_DOM_NODE (list
),
16800 WEBKIT_DOM_NODE (element
),
16803 block
= e_editor_dom_get_parent_block_node_from_child (
16804 WEBKIT_DOM_NODE (selection_start_marker
));
16806 webkit_dom_node_insert_before (
16807 webkit_dom_node_get_parent_node (block
),
16808 WEBKIT_DOM_NODE (list
),
16812 /* Process all blocks that are in the selection one by one */
16813 while (block
&& !after_selection_end
) {
16814 gboolean empty
= FALSE
, block_is_list
;
16816 WebKitDOMNode
*child
, *parent
;
16818 after_selection_end
= webkit_dom_node_contains (
16819 block
, WEBKIT_DOM_NODE (selection_end_marker
));
16821 next_block
= webkit_dom_node_get_next_sibling (
16822 WEBKIT_DOM_NODE (block
));
16824 e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block
));
16825 e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block
));
16827 item
= webkit_dom_document_create_element (document
, "LI", NULL
);
16828 content
= webkit_dom_node_get_text_content (block
);
16830 empty
= !*content
|| (g_strcmp0 (content
, UNICODE_ZERO_WIDTH_SPACE
) == 0);
16833 change_leading_space_to_nbsp (block
);
16834 change_trailing_space_in_block_to_nbsp (block
);
16836 block_is_list
= node_is_list_or_item (block
);
16838 while ((child
= webkit_dom_node_get_first_child (block
))) {
16839 if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child
))
16842 webkit_dom_node_append_child (
16843 WEBKIT_DOM_NODE (block_is_list
? list
: item
), child
, NULL
);
16846 if (!block_is_list
) {
16847 /* We have to use again the hidden space to move caret into newly inserted list */
16849 WebKitDOMElement
*br
;
16851 br
= webkit_dom_document_create_element (
16852 document
, "BR", NULL
);
16853 webkit_dom_node_append_child (
16854 WEBKIT_DOM_NODE (item
), WEBKIT_DOM_NODE (br
), NULL
);
16857 webkit_dom_node_append_child (
16858 WEBKIT_DOM_NODE (list
), WEBKIT_DOM_NODE (item
), NULL
);
16861 parent
= webkit_dom_node_get_parent_node (block
);
16862 remove_node (block
);
16865 /* Remove all parents if previously removed node was the
16866 * only one with text content */
16867 content
= webkit_dom_node_get_text_content (parent
);
16868 while (parent
&& content
&& !*content
) {
16869 WebKitDOMNode
*tmp
= webkit_dom_node_get_parent_node (parent
);
16871 remove_node (parent
);
16875 content
= webkit_dom_node_get_text_content (parent
);
16880 block
= next_block
;
16883 merge_lists_if_possible (WEBKIT_DOM_NODE (list
));
16886 static WebKitDOMElement
*
16887 do_format_change_list_to_list (WebKitDOMElement
*list_to_process
,
16888 WebKitDOMElement
*new_list_template
,
16889 EContentEditorBlockFormat to
)
16891 EContentEditorBlockFormat current_format
;
16893 current_format
= dom_get_list_format_from_node (
16894 WEBKIT_DOM_NODE (list_to_process
));
16895 if (to
== current_format
) {
16896 /* Same format, skip it. */
16897 return list_to_process
;
16898 } else if (current_format
>= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
&&
16899 to
>= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
) {
16900 /* Changing from ordered list type to another ordered list type. */
16901 set_ordered_list_type_to_element (list_to_process
, to
);
16902 return list_to_process
;
16904 WebKitDOMNode
*clone
, *child
;
16906 /* Create new list from template. */
16907 clone
= webkit_dom_node_clone_node_with_error (
16908 WEBKIT_DOM_NODE (new_list_template
), FALSE
, NULL
);
16910 /* Insert it before the list that we are processing. */
16911 webkit_dom_node_insert_before (
16912 webkit_dom_node_get_parent_node (
16913 WEBKIT_DOM_NODE (list_to_process
)),
16915 WEBKIT_DOM_NODE (list_to_process
),
16918 /* Move all it children to the new one. */
16919 while ((child
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (list_to_process
))))
16920 webkit_dom_node_append_child (clone
, child
, NULL
);
16922 remove_node (WEBKIT_DOM_NODE (list_to_process
));
16924 return WEBKIT_DOM_ELEMENT (clone
);
16931 format_change_list_from_list (EEditorPage
*editor_page
,
16932 EContentEditorBlockFormat to
,
16933 gboolean html_mode
)
16935 WebKitDOMDocument
*document
;
16936 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
, *new_list
;
16937 WebKitDOMNode
*source_list
, *source_list_clone
, *current_list
, *item
;
16938 gboolean after_selection_end
= FALSE
;
16940 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
16942 document
= e_editor_page_get_document (editor_page
);
16943 selection_start_marker
= webkit_dom_document_get_element_by_id (
16944 document
, "-x-evo-selection-start-marker");
16945 selection_end_marker
= webkit_dom_document_get_element_by_id (
16946 document
, "-x-evo-selection-end-marker");
16948 if (!selection_start_marker
|| !selection_end_marker
)
16951 /* Copy elements from previous block to list */
16952 item
= get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start_marker
));
16953 source_list
= webkit_dom_node_get_parent_node (item
);
16954 current_list
= source_list
;
16955 source_list_clone
= webkit_dom_node_clone_node_with_error (source_list
, FALSE
, NULL
);
16957 new_list
= create_list_element (editor_page
, to
, 0, html_mode
);
16959 if (element_has_class (WEBKIT_DOM_ELEMENT (source_list
), "-x-evo-indented"))
16960 element_add_class (WEBKIT_DOM_ELEMENT (new_list
), "-x-evo-indented");
16963 gboolean selection_end
;
16964 WebKitDOMNode
*next_item
= webkit_dom_node_get_next_sibling (item
);
16966 selection_end
= webkit_dom_node_contains (
16967 item
, WEBKIT_DOM_NODE (selection_end_marker
));
16969 if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item
)) {
16970 /* Actual node is an item, just copy it. */
16971 webkit_dom_node_append_child (
16972 after_selection_end
?
16973 source_list_clone
: WEBKIT_DOM_NODE (new_list
),
16976 } else if (node_is_list (item
) && !selection_end
&& !after_selection_end
) {
16977 /* Node is a list and it doesn't contain the selection end
16978 * marker, we can process the whole list. */
16980 WebKitDOMNodeList
*list
= NULL
;
16981 WebKitDOMElement
*processed_list
;
16983 list
= webkit_dom_element_query_selector_all (
16984 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
16985 ii
= webkit_dom_node_list_get_length (list
);
16986 g_clear_object (&list
);
16988 /* Process every sublist separately. */
16990 WebKitDOMElement
*list_to_process
;
16992 list_to_process
= webkit_dom_element_query_selector (
16993 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
16994 if (list_to_process
)
16995 do_format_change_list_to_list (list_to_process
, new_list
, to
);
16999 /* Process the current list. */
17000 processed_list
= do_format_change_list_to_list (
17001 WEBKIT_DOM_ELEMENT (item
), new_list
, to
);
17003 webkit_dom_node_append_child (
17004 WEBKIT_DOM_NODE (new_list
),
17005 WEBKIT_DOM_NODE (processed_list
),
17007 } else if (node_is_list (item
) && !after_selection_end
) {
17008 /* Node is a list and it contains the selection end marker,
17009 * thus we have to process it until we find the marker. */
17011 WebKitDOMNodeList
*list
= NULL
;
17013 list
= webkit_dom_element_query_selector_all (
17014 WEBKIT_DOM_ELEMENT (item
), "ol,ul", NULL
);
17015 ii
= webkit_dom_node_list_get_length (list
);
17016 g_clear_object (&list
);
17018 /* No nested lists - process the items. */
17020 WebKitDOMNode
*clone
, *child
;
17022 clone
= webkit_dom_node_clone_node_with_error (
17023 WEBKIT_DOM_NODE (new_list
), FALSE
, NULL
);
17025 webkit_dom_node_append_child (
17026 WEBKIT_DOM_NODE (new_list
), clone
, NULL
);
17028 while ((child
= webkit_dom_node_get_first_child (item
))) {
17029 webkit_dom_node_append_child (clone
, child
, NULL
);
17030 if (webkit_dom_node_contains (child
, WEBKIT_DOM_NODE (selection_end_marker
)))
17034 if (webkit_dom_node_get_first_child (item
))
17035 webkit_dom_node_append_child (
17036 WEBKIT_DOM_NODE (new_list
), item
, NULL
);
17038 remove_node (item
);
17040 gboolean done
= FALSE
;
17041 WebKitDOMNode
*tmp_parent
= WEBKIT_DOM_NODE (new_list
);
17042 WebKitDOMNode
*tmp_item
= WEBKIT_DOM_NODE (item
);
17045 WebKitDOMNode
*clone
, *child
;
17047 clone
= webkit_dom_node_clone_node_with_error (
17048 WEBKIT_DOM_NODE (new_list
), FALSE
, NULL
);
17050 webkit_dom_node_append_child (
17051 tmp_parent
, clone
, NULL
);
17053 while ((child
= webkit_dom_node_get_first_child (tmp_item
))) {
17054 if (!webkit_dom_node_contains (child
, WEBKIT_DOM_NODE (selection_end_marker
))) {
17055 webkit_dom_node_append_child (clone
, child
, NULL
);
17056 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (child
)) {
17057 webkit_dom_node_append_child (clone
, child
, NULL
);
17061 tmp_parent
= clone
;
17069 webkit_dom_node_append_child (
17070 after_selection_end
?
17071 source_list_clone
: WEBKIT_DOM_NODE (new_list
),
17076 if (selection_end
) {
17077 source_list_clone
= webkit_dom_node_clone_node_with_error (current_list
, FALSE
, NULL
);
17078 after_selection_end
= TRUE
;
17082 if (after_selection_end
)
17085 current_list
= webkit_dom_node_get_next_sibling (current_list
);
17086 if (!node_is_list_or_item (current_list
))
17088 if (node_is_list (current_list
)) {
17089 next_item
= webkit_dom_node_get_first_child (current_list
);
17090 if (!node_is_list_or_item (next_item
))
17092 } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (current_list
)) {
17093 next_item
= current_list
;
17094 current_list
= webkit_dom_node_get_parent_node (next_item
);
17101 webkit_dom_node_insert_before (
17102 webkit_dom_node_get_parent_node (source_list
),
17103 WEBKIT_DOM_NODE (source_list_clone
),
17104 webkit_dom_node_get_next_sibling (source_list
),
17107 if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list
)))
17108 webkit_dom_node_insert_before (
17109 webkit_dom_node_get_parent_node (source_list_clone
),
17110 WEBKIT_DOM_NODE (new_list
),
17114 remove_node_if_empty (source_list
);
17115 remove_node_if_empty (source_list_clone
);
17116 remove_node_if_empty (current_list
);
17118 merge_lists_if_possible (WEBKIT_DOM_NODE (new_list
));
17122 format_change_list_to_list (EEditorPage
*editor_page
,
17123 EContentEditorBlockFormat format
,
17124 gboolean html_mode
)
17126 WebKitDOMDocument
*document
;
17127 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
17128 WebKitDOMNode
*prev_list
, *current_list
, *next_list
;
17129 EContentEditorBlockFormat prev
= 0, next
= 0;
17130 gboolean done
= FALSE
, indented
= FALSE
;
17131 gboolean selection_starts_in_first_child
, selection_ends_in_last_child
;
17133 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17135 document
= e_editor_page_get_document (editor_page
);
17136 selection_start_marker
= webkit_dom_document_get_element_by_id (
17137 document
, "-x-evo-selection-start-marker");
17138 selection_end_marker
= webkit_dom_document_get_element_by_id (
17139 document
, "-x-evo-selection-end-marker");
17141 current_list
= get_list_node_from_child (
17142 WEBKIT_DOM_NODE (selection_start_marker
));
17144 prev_list
= get_list_node_from_child (
17145 WEBKIT_DOM_NODE (selection_start_marker
));
17147 next_list
= get_list_node_from_child (
17148 WEBKIT_DOM_NODE (selection_end_marker
));
17150 selection_starts_in_first_child
=
17151 webkit_dom_node_contains (
17152 webkit_dom_node_get_first_child (current_list
),
17153 WEBKIT_DOM_NODE (selection_start_marker
));
17155 selection_ends_in_last_child
=
17156 webkit_dom_node_contains (
17157 webkit_dom_node_get_last_child (current_list
),
17158 WEBKIT_DOM_NODE (selection_end_marker
));
17160 indented
= element_has_class (WEBKIT_DOM_ELEMENT (current_list
), "-x-evo-indented");
17162 if (!prev_list
|| !next_list
|| indented
) {
17163 format_change_list_from_list (editor_page
, format
, html_mode
);
17167 if (webkit_dom_node_is_same_node (prev_list
, next_list
)) {
17168 prev_list
= webkit_dom_node_get_previous_sibling (
17169 webkit_dom_node_get_parent_node (
17170 webkit_dom_node_get_parent_node (
17171 WEBKIT_DOM_NODE (selection_start_marker
))));
17172 next_list
= webkit_dom_node_get_next_sibling (
17173 webkit_dom_node_get_parent_node (
17174 webkit_dom_node_get_parent_node (
17175 WEBKIT_DOM_NODE (selection_end_marker
))));
17176 if (!prev_list
|| !next_list
) {
17177 format_change_list_from_list (editor_page
, format
, html_mode
);
17182 prev
= dom_get_list_format_from_node (prev_list
);
17183 next
= dom_get_list_format_from_node (next_list
);
17185 if (format
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17186 if (format
== prev
&& prev
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17187 if (selection_starts_in_first_child
&& selection_ends_in_last_child
) {
17189 merge_list_into_list (current_list
, prev_list
, FALSE
);
17192 if (format
== next
&& next
!= E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
) {
17193 if (selection_starts_in_first_child
&& selection_ends_in_last_child
) {
17195 merge_list_into_list (next_list
, prev_list
, FALSE
);
17203 format_change_list_from_list (editor_page
, format
, html_mode
);
17207 * e_html_editor_selection_set_block_format:
17208 * @selection: an #EEditorSelection
17209 * @format: an #EContentEditorBlockFormat value
17211 * Changes block format of current paragraph to @format.
17214 e_editor_dom_selection_set_block_format (EEditorPage
*editor_page
,
17215 EContentEditorBlockFormat format
)
17217 WebKitDOMDocument
*document
;
17218 WebKitDOMRange
*range
= NULL
;
17219 EContentEditorBlockFormat current_format
;
17220 EContentEditorAlignment current_alignment
;
17221 EEditorUndoRedoManager
*manager
;
17222 EEditorHistoryEvent
*ev
= NULL
;
17223 const gchar
*value
;
17224 gboolean from_list
= FALSE
, to_list
= FALSE
, html_mode
= FALSE
;
17226 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17228 document
= e_editor_page_get_document (editor_page
);
17229 current_format
= e_editor_dom_selection_get_block_format (editor_page
);
17230 if (current_format
== format
)
17234 case E_CONTENT_EDITOR_BLOCK_FORMAT_H1
:
17237 case E_CONTENT_EDITOR_BLOCK_FORMAT_H2
:
17240 case E_CONTENT_EDITOR_BLOCK_FORMAT_H3
:
17243 case E_CONTENT_EDITOR_BLOCK_FORMAT_H4
:
17246 case E_CONTENT_EDITOR_BLOCK_FORMAT_H5
:
17249 case E_CONTENT_EDITOR_BLOCK_FORMAT_H6
:
17252 case E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH
:
17255 case E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
:
17258 case E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS
:
17261 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST
:
17262 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
:
17263 case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN
:
17267 case E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
:
17271 case E_CONTENT_EDITOR_BLOCK_FORMAT_NONE
:
17277 html_mode
= e_editor_page_get_html_mode (editor_page
);
17280 current_format
>= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST
;
17282 range
= e_editor_dom_get_current_range (editor_page
);
17286 current_alignment
= e_editor_page_get_alignment (editor_page
);
17288 e_editor_dom_selection_save (editor_page
);
17290 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17291 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17292 ev
= g_new0 (EEditorHistoryEvent
, 1);
17293 ev
->type
= HISTORY_BLOCK_FORMAT
;
17295 e_editor_dom_selection_get_coordinates (editor_page
,
17296 &ev
->before
.start
.x
,
17297 &ev
->before
.start
.y
,
17299 &ev
->before
.end
.y
);
17301 ev
->data
.style
.from
= current_format
;
17302 ev
->data
.style
.to
= format
;
17305 g_clear_object (&range
);
17307 if (current_format
== E_CONTENT_EDITOR_BLOCK_FORMAT_PRE
) {
17308 WebKitDOMElement
*selection_marker
;
17310 selection_marker
= webkit_dom_document_get_element_by_id (
17311 document
, "-x-evo-selection-start-marker");
17312 if (selection_marker
)
17313 change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker
));
17314 selection_marker
= webkit_dom_document_get_element_by_id (
17315 document
, "-x-evo-selection-end-marker");
17316 if (selection_marker
)
17317 change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker
));
17320 if (from_list
&& to_list
)
17321 format_change_list_to_list (editor_page
, format
, html_mode
);
17323 if (!from_list
&& !to_list
)
17324 format_change_block_to_block (editor_page
, format
, value
);
17326 if (from_list
&& !to_list
)
17327 format_change_list_to_block (editor_page
, format
, value
);
17329 if (!from_list
&& to_list
)
17330 format_change_block_to_list (editor_page
, format
);
17332 e_editor_dom_selection_restore (editor_page
);
17334 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17336 /* When changing the format we need to re-set the alignment */
17337 e_editor_dom_selection_set_alignment (editor_page
, current_alignment
);
17339 e_editor_page_emit_content_changed (editor_page
);
17342 e_editor_dom_selection_get_coordinates (editor_page
,
17343 &ev
->after
.start
.x
,
17344 &ev
->after
.start
.y
,
17347 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17352 * e_html_editor_selection_get_background_color:
17353 * @selection: an #EEditorSelection
17355 * Returns background color of currently selected text or letter at current
17358 * Returns: A string with code of current background color.
17361 e_editor_dom_selection_get_background_color (EEditorPage
*editor_page
)
17363 WebKitDOMNode
*ancestor
;
17364 WebKitDOMRange
*range
= NULL
;
17365 WebKitDOMCSSStyleDeclaration
*css
= NULL
;
17368 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17370 range
= e_editor_dom_get_current_range (editor_page
);
17371 ancestor
= webkit_dom_range_get_common_ancestor_container (range
, NULL
);
17372 css
= webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor
));
17374 g_free (selection->priv->background_color);
17375 selection->priv->background_color =
17376 webkit_dom_css_style_declaration_get_property_value (
17377 css, "background-color");*/
17379 value
= webkit_dom_css_style_declaration_get_property_value (css
, "background-color");
17381 g_clear_object (&css
);
17382 g_clear_object (&range
);
17388 * e_html_editor_selection_set_background_color:
17389 * @selection: an #EEditorSelection
17390 * @color: code of new background color to set
17392 * Changes background color of current selection or letter at current cursor
17393 * position to @color.
17396 e_editor_dom_selection_set_background_color (EEditorPage
*editor_page
,
17397 const gchar
*color
)
17399 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17401 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR
, color
);
17405 * e_html_editor_selection_get_alignment:
17406 * @selection: #an EEditorSelection
17408 * Returns alignment of current paragraph
17410 * Returns: #EContentEditorAlignment
17412 EContentEditorAlignment
17413 e_editor_dom_selection_get_alignment (EEditorPage
*editor_page
)
17415 WebKitDOMCSSStyleDeclaration
*style
= NULL
;
17416 WebKitDOMElement
*element
;
17417 WebKitDOMNode
*node
;
17418 WebKitDOMRange
*range
= NULL
;
17419 EContentEditorAlignment alignment
;
17422 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), E_CONTENT_EDITOR_ALIGNMENT_LEFT
);
17424 range
= e_editor_dom_get_current_range (editor_page
);
17426 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17430 node
= webkit_dom_range_get_start_container (range
, NULL
);
17431 g_clear_object (&range
);
17433 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17437 if (WEBKIT_DOM_IS_ELEMENT (node
))
17438 element
= WEBKIT_DOM_ELEMENT (node
);
17440 element
= webkit_dom_node_get_parent_element (node
);
17442 if (element_has_class (element
, "-x-evo-align-right")) {
17443 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17445 } else if (element_has_class (element
, "-x-evo-align-center")) {
17446 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17450 style
= webkit_dom_element_get_style (element
);
17451 value
= webkit_dom_css_style_declaration_get_property_value (style
, "text-align");
17453 if (!value
|| !*value
||
17454 (g_ascii_strncasecmp (value
, "left", 4) == 0)) {
17455 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17456 } else if (g_ascii_strncasecmp (value
, "center", 6) == 0) {
17457 alignment
= E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17458 } else if (g_ascii_strncasecmp (value
, "right", 5) == 0) {
17459 alignment
= E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17461 alignment
= E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17464 g_clear_object (&style
);
17472 set_block_alignment (WebKitDOMElement
*element
,
17473 const gchar
*class)
17475 WebKitDOMElement
*parent
;
17477 element_remove_class (element
, "-x-evo-align-center");
17478 element_remove_class (element
, "-x-evo-align-right");
17479 element_add_class (element
, class);
17480 parent
= webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element
));
17481 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17482 element_remove_class (parent
, "-x-evo-align-center");
17483 element_remove_class (parent
, "-x-evo-align-right");
17484 parent
= webkit_dom_node_get_parent_element (
17485 WEBKIT_DOM_NODE (parent
));
17490 * e_html_editor_selection_set_alignment:
17491 * @selection: an #EEditorSelection
17492 * @alignment: an #EContentEditorAlignment value to apply
17494 * Sets alignment of current paragraph to give @alignment.
17497 e_editor_dom_selection_set_alignment (EEditorPage
*editor_page
,
17498 EContentEditorAlignment alignment
)
17500 WebKitDOMDocument
*document
;
17501 WebKitDOMElement
*selection_start_marker
, *selection_end_marker
;
17502 WebKitDOMNode
*block
;
17503 EContentEditorAlignment current_alignment
;
17504 EEditorUndoRedoManager
*manager
;
17505 EEditorHistoryEvent
*ev
= NULL
;
17506 gboolean after_selection_end
= FALSE
;
17507 const gchar
*class = "";
17509 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17511 document
= e_editor_page_get_document (editor_page
);
17512 current_alignment
= e_editor_page_get_alignment (editor_page
);
17514 if (current_alignment
== alignment
)
17517 switch (alignment
) {
17518 case E_CONTENT_EDITOR_ALIGNMENT_CENTER
:
17519 class = "-x-evo-align-center";
17522 case E_CONTENT_EDITOR_ALIGNMENT_LEFT
:
17525 case E_CONTENT_EDITOR_ALIGNMENT_RIGHT
:
17526 class = "-x-evo-align-right";
17530 e_editor_dom_selection_save (editor_page
);
17532 selection_start_marker
= webkit_dom_document_get_element_by_id (
17533 document
, "-x-evo-selection-start-marker");
17534 selection_end_marker
= webkit_dom_document_get_element_by_id (
17535 document
, "-x-evo-selection-end-marker");
17537 if (!selection_start_marker
)
17540 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17541 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17542 ev
= g_new0 (EEditorHistoryEvent
, 1);
17543 ev
->type
= HISTORY_ALIGNMENT
;
17545 e_editor_dom_selection_get_coordinates (editor_page
,
17546 &ev
->before
.start
.x
,
17547 &ev
->before
.start
.y
,
17549 &ev
->before
.end
.y
);
17550 ev
->data
.style
.from
= current_alignment
;
17551 ev
->data
.style
.to
= alignment
;
17554 block
= e_editor_dom_get_parent_block_node_from_child (
17555 WEBKIT_DOM_NODE (selection_start_marker
));
17557 while (block
&& !after_selection_end
) {
17558 WebKitDOMNode
*next_block
;
17560 next_block
= webkit_dom_node_get_next_sibling (block
);
17562 after_selection_end
= webkit_dom_node_contains (
17563 block
, WEBKIT_DOM_NODE (selection_end_marker
));
17565 if (element_has_class (WEBKIT_DOM_ELEMENT (block
), "-x-evo-indented")) {
17567 WebKitDOMNodeList
*list
= NULL
;
17569 list
= webkit_dom_element_query_selector_all (
17570 WEBKIT_DOM_ELEMENT (block
),
17571 ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
17573 for (ii
= webkit_dom_node_list_get_length (list
); ii
--;) {
17574 WebKitDOMNode
*item
= webkit_dom_node_list_item (list
, ii
);
17576 set_block_alignment (WEBKIT_DOM_ELEMENT (item
), class);
17578 after_selection_end
= webkit_dom_node_contains (
17579 item
, WEBKIT_DOM_NODE (selection_end_marker
));
17580 if (after_selection_end
)
17584 g_clear_object (&list
);
17586 set_block_alignment (WEBKIT_DOM_ELEMENT (block
), class);
17589 block
= next_block
;
17593 e_editor_dom_selection_get_coordinates (editor_page
,
17594 &ev
->after
.start
.x
,
17595 &ev
->after
.start
.y
,
17598 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17601 e_editor_dom_selection_restore (editor_page
);
17603 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17604 e_editor_page_emit_content_changed (editor_page
);
17608 e_editor_dom_insert_replace_all_history_event (EEditorPage
*editor_page
,
17609 const gchar
*search_text
,
17610 const gchar
*replacement
)
17612 EEditorUndoRedoManager
*manager
;
17614 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17616 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17618 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17619 EEditorHistoryEvent
*ev
= g_new0 (EEditorHistoryEvent
, 1);
17620 ev
->type
= HISTORY_REPLACE_ALL
;
17622 ev
->data
.string
.from
= g_strdup (search_text
);
17623 ev
->data
.string
.to
= g_strdup (replacement
);
17625 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17630 * e_html_editor_selection_replace:
17631 * @selection: an #EEditorSelection
17632 * @replacement: a string to replace current selection with
17634 * Replaces currently selected text with @replacement.
17637 e_editor_dom_selection_replace (EEditorPage
*editor_page
,
17638 const gchar
*replacement
)
17640 EEditorHistoryEvent
*ev
= NULL
;
17641 EEditorUndoRedoManager
*manager
;
17642 WebKitDOMRange
*range
= NULL
;
17644 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17646 manager
= e_editor_page_get_undo_redo_manager (editor_page
);
17648 if (!(range
= e_editor_dom_get_current_range (editor_page
)) ||
17649 e_editor_dom_selection_is_collapsed (editor_page
))
17652 if (!e_editor_undo_redo_manager_is_operation_in_progress (manager
)) {
17653 ev
= g_new0 (EEditorHistoryEvent
, 1);
17654 ev
->type
= HISTORY_REPLACE
;
17656 e_editor_dom_selection_get_coordinates (editor_page
,
17657 &ev
->before
.start
.x
,
17658 &ev
->before
.start
.y
,
17660 &ev
->before
.end
.y
);
17662 ev
->data
.string
.from
= webkit_dom_range_get_text (range
);
17663 ev
->data
.string
.to
= g_strdup (replacement
);
17666 g_clear_object (&range
);
17668 e_editor_dom_exec_command (editor_page
, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT
, replacement
);
17671 e_editor_dom_selection_get_coordinates (editor_page
,
17672 &ev
->after
.start
.x
,
17673 &ev
->after
.start
.y
,
17677 e_editor_undo_redo_manager_insert_history_event (manager
, ev
);
17680 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17682 e_editor_page_emit_content_changed (editor_page
);
17686 * e_html_editor_selection_replace_caret_word:
17687 * @selection: an #EEditorSelection
17688 * @replacement: a string to replace current caret word with
17690 * Replaces current word under cursor with @replacement.
17693 e_editor_dom_replace_caret_word (EEditorPage
*editor_page
,
17694 const gchar
*replacement
)
17696 WebKitDOMDocument
*document
;
17697 WebKitDOMDOMWindow
*dom_window
= NULL
;
17698 WebKitDOMDOMSelection
*dom_selection
= NULL
;
17699 WebKitDOMDocumentFragment
*fragment
;
17700 WebKitDOMNode
*node
;
17701 WebKitDOMRange
*range
= NULL
;
17703 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17705 document
= e_editor_page_get_document (editor_page
);
17706 dom_window
= webkit_dom_document_get_default_view (document
);
17707 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
17708 g_clear_object (&dom_window
);
17710 e_editor_page_emit_content_changed (editor_page
);
17711 range
= e_editor_dom_get_current_range (editor_page
);
17712 webkit_dom_range_expand (range
, "word", NULL
);
17713 webkit_dom_dom_selection_add_range (dom_selection
, range
);
17715 fragment
= webkit_dom_range_extract_contents (range
, NULL
);
17717 /* Get the text node to replace and leave other formatting nodes
17718 * untouched (font color, boldness, ...). */
17719 webkit_dom_node_normalize (WEBKIT_DOM_NODE (fragment
));
17720 node
= webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment
));
17721 if (!WEBKIT_DOM_IS_TEXT (node
)) {
17722 while (node
&& WEBKIT_DOM_IS_ELEMENT (node
))
17723 node
= webkit_dom_node_get_first_child (node
);
17726 if (node
&& WEBKIT_DOM_IS_TEXT (node
)) {
17727 WebKitDOMText
*text
;
17729 /* Replace the word */
17730 text
= webkit_dom_document_create_text_node (document
, replacement
);
17731 webkit_dom_node_replace_child (
17732 webkit_dom_node_get_parent_node (node
),
17733 WEBKIT_DOM_NODE (text
),
17737 /* Insert the word on current location. */
17738 webkit_dom_range_insert_node (range
, WEBKIT_DOM_NODE (fragment
), NULL
);
17740 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
17743 e_editor_dom_force_spell_check_for_current_paragraph (editor_page
);
17745 g_clear_object (&range
);
17746 g_clear_object (&dom_selection
);
17750 * e_html_editor_selection_get_caret_word:
17751 * @selection: an #EEditorSelection
17753 * Returns word under cursor.
17755 * Returns: A newly allocated string with current caret word or @NULL when there
17756 * is no text under cursor or when selection is active. [transfer-full].
17759 e_editor_dom_get_caret_word (EEditorPage
*editor_page
)
17762 WebKitDOMRange
*range
= NULL
, *range_clone
= NULL
;
17764 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17766 range
= e_editor_dom_get_current_range (editor_page
);
17768 /* Don't operate on the visible selection */
17769 range_clone
= webkit_dom_range_clone_range (range
, NULL
);
17770 webkit_dom_range_expand (range_clone
, "word", NULL
);
17771 word
= webkit_dom_range_to_string (range_clone
, NULL
);
17773 g_clear_object (&range
);
17774 g_clear_object (&range_clone
);
17780 * e_html_editor_selection_get_list_alignment_from_node:
17781 * @node: #an WebKitDOMNode
17783 * Returns alignment of given list.
17785 * Returns: #EContentEditorAlignment
17787 EContentEditorAlignment
17788 e_editor_dom_get_list_alignment_from_node (WebKitDOMNode
*node
)
17790 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-center"))
17791 return E_CONTENT_EDITOR_ALIGNMENT_CENTER
;
17792 if (element_has_class (WEBKIT_DOM_ELEMENT (node
), "-x-evo-align-right"))
17793 return E_CONTENT_EDITOR_ALIGNMENT_RIGHT
;
17795 return E_CONTENT_EDITOR_ALIGNMENT_LEFT
;
17799 e_editor_dom_prepare_paragraph (EEditorPage
*editor_page
,
17800 gboolean with_selection
)
17802 WebKitDOMDocument
*document
;
17803 WebKitDOMElement
*element
, *paragraph
;
17805 g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page
), NULL
);
17807 document
= e_editor_page_get_document (editor_page
);
17808 paragraph
= e_editor_dom_get_paragraph_element (editor_page
, -1, 0);
17810 if (with_selection
)
17811 dom_add_selection_markers_into_element_start (
17812 document
, paragraph
, NULL
, NULL
);
17814 element
= webkit_dom_document_create_element (document
, "BR", NULL
);
17816 webkit_dom_node_append_child (
17817 WEBKIT_DOM_NODE (paragraph
), WEBKIT_DOM_NODE (element
), NULL
);
17823 e_editor_dom_selection_set_on_point (EEditorPage
*editor_page
,
17827 WebKitDOMDocument
*document
;
17828 WebKitDOMRange
*range
= NULL
;
17829 WebKitDOMDOMWindow
*dom_window
= NULL
;
17830 WebKitDOMDOMSelection
*dom_selection
= NULL
;
17832 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17834 document
= e_editor_page_get_document (editor_page
);
17835 dom_window
= webkit_dom_document_get_default_view (document
);
17836 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
17838 range
= webkit_dom_document_caret_range_from_point (document
, x
, y
);
17839 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
17840 webkit_dom_dom_selection_add_range (dom_selection
, range
);
17842 g_clear_object (&range
);
17843 g_clear_object (&dom_selection
);
17844 g_clear_object (&dom_window
);
17848 e_editor_dom_selection_get_coordinates (EEditorPage
*editor_page
,
17854 WebKitDOMDocument
*document
;
17855 WebKitDOMElement
*element
, *parent
;
17856 gboolean created_selection_markers
= FALSE
;
17857 guint local_x
= 0, local_y
= 0;
17859 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17860 g_return_if_fail (start_x
!= NULL
);
17861 g_return_if_fail (start_y
!= NULL
);
17862 g_return_if_fail (end_x
!= NULL
);
17863 g_return_if_fail (end_y
!= NULL
);
17865 document
= e_editor_page_get_document (editor_page
);
17866 element
= webkit_dom_document_get_element_by_id (
17867 document
, "-x-evo-selection-start-marker");
17869 created_selection_markers
= TRUE
;
17870 e_editor_dom_selection_save (editor_page
);
17871 element
= webkit_dom_document_get_element_by_id (
17872 document
, "-x-evo-selection-start-marker");
17878 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17879 local_x
+= (guint
) webkit_dom_element_get_offset_left (parent
);
17880 local_y
+= (guint
) webkit_dom_element_get_offset_top (parent
);
17881 parent
= webkit_dom_element_get_offset_parent (parent
);
17884 *start_x
= local_x
;
17885 *start_y
= local_y
;
17887 if (e_editor_dom_selection_is_collapsed (editor_page
)) {
17891 if (created_selection_markers
)
17892 e_editor_dom_selection_restore (editor_page
);
17897 element
= webkit_dom_document_get_element_by_id (
17898 document
, "-x-evo-selection-end-marker");
17904 while (parent
&& !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent
)) {
17905 local_x
+= (guint
) webkit_dom_element_get_offset_left (parent
);
17906 local_y
+= (guint
) webkit_dom_element_get_offset_top (parent
);
17907 parent
= webkit_dom_element_get_offset_parent (parent
);
17913 if (created_selection_markers
)
17914 e_editor_dom_selection_restore (editor_page
);
17917 /* Workaround for bug 749712 on the Evolution side. The cause of the bug
17918 * is that WebKit is having problems determining the right line height
17919 * for some fonts and font sizes (the right and wrong value differ by 1).
17920 * To fix this we will add an extra one to the final top offset. This is
17921 * safe to do even for fonts and font sizes that don't behave badly as we
17922 * will still get the right element as we use fonts bigger than 1 pixel. */
17928 e_editor_dom_get_range_for_point (WebKitDOMDocument
*document
,
17929 EEditorSelectionPoint point
)
17931 glong scroll_left
, scroll_top
;
17932 WebKitDOMHTMLElement
*body
;
17933 WebKitDOMRange
*range
= NULL
;
17935 body
= webkit_dom_document_get_body (document
);
17936 scroll_left
= webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body
));
17937 scroll_top
= webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body
));
17939 range
= webkit_dom_document_caret_range_from_point (
17940 document
, point
.x
- scroll_left
, point
.y
- scroll_top
);
17942 /* The point is outside the viewport, scroll to it. */
17944 WebKitDOMDOMWindow
*dom_window
= NULL
;
17946 dom_window
= webkit_dom_document_get_default_view (document
);
17947 webkit_dom_dom_window_scroll_to (dom_window
, point
.x
, point
.y
);
17949 scroll_left
= webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body
));
17950 scroll_top
= webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body
));
17951 range
= webkit_dom_document_caret_range_from_point (
17952 document
, point
.x
- scroll_left
, point
.y
- scroll_top
);
17953 g_clear_object (&dom_window
);
17960 e_editor_dom_selection_restore_to_history_event_state (EEditorPage
*editor_page
,
17961 EEditorSelection selection_state
)
17963 WebKitDOMDocument
*document
;
17964 WebKitDOMDOMWindow
*dom_window
= NULL
;
17965 WebKitDOMDOMSelection
*dom_selection
= NULL
;
17966 WebKitDOMElement
*element
, *tmp
;
17967 WebKitDOMRange
*range
= NULL
;
17968 gboolean was_collapsed
= FALSE
;
17970 g_return_if_fail (E_IS_EDITOR_PAGE (editor_page
));
17972 document
= e_editor_page_get_document (editor_page
);
17973 dom_window
= webkit_dom_document_get_default_view (document
);
17974 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
17975 g_clear_object (&dom_window
);
17977 /* Restore the selection how it was before the event occured. */
17978 range
= e_editor_dom_get_range_for_point (document
, selection_state
.start
);
17979 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
17980 webkit_dom_dom_selection_add_range (dom_selection
, range
);
17981 g_clear_object (&range
);
17983 was_collapsed
= selection_state
.start
.x
== selection_state
.end
.x
;
17984 was_collapsed
= was_collapsed
&& selection_state
.start
.y
== selection_state
.end
.y
;
17985 if (was_collapsed
) {
17986 g_clear_object (&dom_selection
);
17990 e_editor_dom_selection_save (editor_page
);
17992 element
= webkit_dom_document_get_element_by_id (
17993 document
, "-x-evo-selection-end-marker");
17995 remove_node (WEBKIT_DOM_NODE (element
));
17997 element
= webkit_dom_document_get_element_by_id (
17998 document
, "-x-evo-selection-start-marker");
18000 webkit_dom_element_remove_attribute (element
, "id");
18002 range
= e_editor_dom_get_range_for_point (document
, selection_state
.end
);
18003 webkit_dom_dom_selection_remove_all_ranges (dom_selection
);
18004 webkit_dom_dom_selection_add_range (dom_selection
, range
);
18005 g_clear_object (&range
);
18007 e_editor_dom_selection_save (editor_page
);
18009 tmp
= webkit_dom_document_get_element_by_id (
18010 document
, "-x-evo-selection-start-marker");
18012 remove_node (WEBKIT_DOM_NODE (tmp
));
18014 webkit_dom_element_set_id (
18015 element
, "-x-evo-selection-start-marker");
18017 e_editor_dom_selection_restore (editor_page
);
18019 g_clear_object (&dom_selection
);