The main cause was that composer went to busy loop when it was trying to wrap a line with word that was longer that line length (usually 72 characters). Now we will force the split at line length to avoid this situation. Another thing was that when we wrapped the line it could happen that a wrong text node will be chosen as a next node to split.
7427 lines
211 KiB
C
7427 lines
211 KiB
C
/*
|
|
* e-html-editor-selection.c
|
|
*
|
|
* Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "e-html-editor-selection.h"
|
|
#include "e-html-editor-view.h"
|
|
#include "e-html-editor.h"
|
|
#include "e-html-editor-utils.h"
|
|
|
|
#include <e-util/e-util.h>
|
|
|
|
#include <webkit/webkit.h>
|
|
#include <webkit/webkitdom.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#define E_HTML_EDITOR_SELECTION_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE \
|
|
((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionPrivate))
|
|
|
|
/**
|
|
* EHTMLEditorSelection
|
|
*
|
|
* The #EHTMLEditorSelection object represents current position of the cursor
|
|
* with the editor or current text selection within the editor. To obtain
|
|
* valid #EHTMLEditorSelection, call e_html_editor_view_get_selection().
|
|
*/
|
|
|
|
struct _EHTMLEditorSelectionPrivate {
|
|
|
|
GWeakRef html_editor_view;
|
|
gulong selection_changed_handler_id;
|
|
gboolean selection_changed_callbacks_blocked;
|
|
|
|
gchar *text;
|
|
|
|
gboolean is_bold;
|
|
gboolean is_italic;
|
|
gboolean is_underline;
|
|
gboolean is_monospaced;
|
|
gboolean is_strikethrough;
|
|
|
|
gchar *background_color;
|
|
gchar *font_color;
|
|
gchar *font_family;
|
|
|
|
gulong selection_offset;
|
|
|
|
gint word_wrap_length;
|
|
guint font_size;
|
|
|
|
EHTMLEditorSelectionAlignment alignment;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_ALIGNMENT,
|
|
PROP_BACKGROUND_COLOR,
|
|
PROP_BLOCK_FORMAT,
|
|
PROP_BOLD,
|
|
PROP_HTML_EDITOR_VIEW,
|
|
PROP_FONT_COLOR,
|
|
PROP_FONT_NAME,
|
|
PROP_FONT_SIZE,
|
|
PROP_INDENTED,
|
|
PROP_ITALIC,
|
|
PROP_MONOSPACED,
|
|
PROP_STRIKETHROUGH,
|
|
PROP_SUBSCRIPT,
|
|
PROP_SUPERSCRIPT,
|
|
PROP_TEXT,
|
|
PROP_UNDERLINE
|
|
};
|
|
|
|
static const GdkRGBA black = { 0, 0, 0, 1 };
|
|
|
|
G_DEFINE_TYPE (
|
|
EHTMLEditorSelection,
|
|
e_html_editor_selection,
|
|
G_TYPE_OBJECT
|
|
);
|
|
|
|
static WebKitDOMRange *
|
|
html_editor_selection_get_current_range (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range = NULL;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_val_if_fail (view != NULL, NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
if (!dom_window)
|
|
return NULL;
|
|
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
if (!dom_selection) {
|
|
g_object_unref (dom_window);
|
|
return NULL;
|
|
}
|
|
|
|
if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
|
|
goto exit;
|
|
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
exit:
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
|
|
return range;
|
|
}
|
|
|
|
static gboolean
|
|
get_has_style (EHTMLEditorSelection *selection,
|
|
const gchar *style_tag)
|
|
{
|
|
WebKitDOMNode *node;
|
|
WebKitDOMElement *element;
|
|
WebKitDOMRange *range;
|
|
gboolean result;
|
|
gint tag_len;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return FALSE;
|
|
|
|
node = webkit_dom_range_get_start_container (range, NULL);
|
|
if (WEBKIT_DOM_IS_ELEMENT (node))
|
|
element = WEBKIT_DOM_ELEMENT (node);
|
|
else
|
|
element = webkit_dom_node_get_parent_element (node);
|
|
g_object_unref (range);
|
|
|
|
tag_len = strlen (style_tag);
|
|
result = FALSE;
|
|
while (!result && element) {
|
|
gchar *element_tag;
|
|
gboolean accept_citation = FALSE;
|
|
|
|
element_tag = webkit_dom_element_get_tag_name (element);
|
|
|
|
if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) {
|
|
accept_citation = TRUE;
|
|
result = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element);
|
|
if (element_has_class (element, "-x-evo-indented"))
|
|
result = FALSE;
|
|
} else {
|
|
result = ((tag_len == strlen (element_tag)) &&
|
|
(g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0));
|
|
}
|
|
|
|
/* Special case: <blockquote type=cite> marks quotation, while
|
|
* just <blockquote> is used for indentation. If the <blockquote>
|
|
* has type=cite, then ignore it unless style_tag is "citation" */
|
|
if (result && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element)) {
|
|
if (webkit_dom_element_has_attribute (element, "type")) {
|
|
gchar *type;
|
|
type = webkit_dom_element_get_attribute (element, "type");
|
|
if (!accept_citation && (g_ascii_strncasecmp (type, "cite", 4) == 0)) {
|
|
result = FALSE;
|
|
}
|
|
g_free (type);
|
|
} else {
|
|
if (accept_citation)
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
g_free (element_tag);
|
|
|
|
if (result)
|
|
break;
|
|
|
|
element = webkit_dom_node_get_parent_element (
|
|
WEBKIT_DOM_NODE (element));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static gchar *
|
|
get_font_property (EHTMLEditorSelection *selection,
|
|
const gchar *font_property)
|
|
{
|
|
WebKitDOMRange *range;
|
|
WebKitDOMNode *node;
|
|
WebKitDOMElement *element;
|
|
gchar *value;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return NULL;
|
|
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
g_object_unref (range);
|
|
element = e_html_editor_dom_node_find_parent_element (node, "FONT");
|
|
if (!element)
|
|
return NULL;
|
|
|
|
g_object_get (G_OBJECT (element), font_property, &value, NULL);
|
|
|
|
return value;
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_selection_changed_cb (WebKitWebView *web_view,
|
|
EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMRange *range = NULL;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return;
|
|
g_object_unref (range);
|
|
|
|
g_object_freeze_notify (G_OBJECT (selection));
|
|
|
|
g_object_notify (G_OBJECT (selection), "alignment");
|
|
g_object_notify (G_OBJECT (selection), "block-format");
|
|
g_object_notify (G_OBJECT (selection), "indented");
|
|
g_object_notify (G_OBJECT (selection), "text");
|
|
|
|
if (!e_html_editor_view_get_html_mode (E_HTML_EDITOR_VIEW (web_view)))
|
|
goto out;
|
|
|
|
g_object_notify (G_OBJECT (selection), "background-color");
|
|
g_object_notify (G_OBJECT (selection), "bold");
|
|
g_object_notify (G_OBJECT (selection), "font-name");
|
|
g_object_notify (G_OBJECT (selection), "font-size");
|
|
g_object_notify (G_OBJECT (selection), "font-color");
|
|
g_object_notify (G_OBJECT (selection), "italic");
|
|
g_object_notify (G_OBJECT (selection), "monospaced");
|
|
g_object_notify (G_OBJECT (selection), "strikethrough");
|
|
g_object_notify (G_OBJECT (selection), "subscript");
|
|
g_object_notify (G_OBJECT (selection), "superscript");
|
|
g_object_notify (G_OBJECT (selection), "underline");
|
|
|
|
out:
|
|
g_object_thaw_notify (G_OBJECT (selection));
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_block_selection_changed (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (!selection->priv->selection_changed_callbacks_blocked) {
|
|
EHTMLEditorView *view;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_signal_handlers_block_by_func (
|
|
view, html_editor_selection_selection_changed_cb, selection);
|
|
g_object_unref (view);
|
|
selection->priv->selection_changed_callbacks_blocked = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_unblock_selection_changed (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (selection->priv->selection_changed_callbacks_blocked) {
|
|
EHTMLEditorView *view;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_signal_handlers_unblock_by_func (
|
|
view, html_editor_selection_selection_changed_cb, selection);
|
|
g_object_unref (view);
|
|
selection->priv->selection_changed_callbacks_blocked = FALSE;
|
|
|
|
html_editor_selection_selection_changed_cb (WEBKIT_WEB_VIEW (view), selection);
|
|
}
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_set_html_editor_view (EHTMLEditorSelection *selection,
|
|
EHTMLEditorView *view)
|
|
{
|
|
gulong handler_id;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
|
|
|
|
g_weak_ref_set (&selection->priv->html_editor_view, view);
|
|
|
|
handler_id = g_signal_connect (
|
|
view, "selection-changed",
|
|
G_CALLBACK (html_editor_selection_selection_changed_cb),
|
|
selection);
|
|
|
|
selection->priv->selection_changed_handler_id = handler_id;
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkRGBA rgba = { 0 };
|
|
|
|
switch (property_id) {
|
|
case PROP_ALIGNMENT:
|
|
g_value_set_int (
|
|
value,
|
|
e_html_editor_selection_get_alignment (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_BACKGROUND_COLOR:
|
|
g_value_set_string (
|
|
value,
|
|
e_html_editor_selection_get_background_color (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_BLOCK_FORMAT:
|
|
g_value_set_int (
|
|
value,
|
|
e_html_editor_selection_get_block_format (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_BOLD:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_bold (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_HTML_EDITOR_VIEW:
|
|
g_value_take_object (
|
|
value,
|
|
e_html_editor_selection_ref_html_editor_view (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_FONT_COLOR:
|
|
e_html_editor_selection_get_font_color (
|
|
E_HTML_EDITOR_SELECTION (object), &rgba);
|
|
g_value_set_boxed (value, &rgba);
|
|
return;
|
|
|
|
case PROP_FONT_NAME:
|
|
g_value_set_string (
|
|
value,
|
|
e_html_editor_selection_get_font_name (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_FONT_SIZE:
|
|
g_value_set_int (
|
|
value,
|
|
e_html_editor_selection_get_font_size (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_INDENTED:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_indented (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_ITALIC:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_italic (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_MONOSPACED:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_monospaced (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_STRIKETHROUGH:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_strikethrough (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_SUBSCRIPT:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_subscript (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_SUPERSCRIPT:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_superscript (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
|
|
case PROP_TEXT:
|
|
g_value_set_string (
|
|
value,
|
|
e_html_editor_selection_get_string (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
break;
|
|
|
|
case PROP_UNDERLINE:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_html_editor_selection_is_underline (
|
|
E_HTML_EDITOR_SELECTION (object)));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_ALIGNMENT:
|
|
e_html_editor_selection_set_alignment (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_int (value));
|
|
return;
|
|
|
|
case PROP_BACKGROUND_COLOR:
|
|
e_html_editor_selection_set_background_color (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_string (value));
|
|
return;
|
|
|
|
case PROP_BOLD:
|
|
e_html_editor_selection_set_bold (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_HTML_EDITOR_VIEW:
|
|
html_editor_selection_set_html_editor_view (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_object (value));
|
|
return;
|
|
|
|
case PROP_FONT_COLOR:
|
|
e_html_editor_selection_set_font_color (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boxed (value));
|
|
return;
|
|
|
|
case PROP_BLOCK_FORMAT:
|
|
e_html_editor_selection_set_block_format (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_int (value));
|
|
return;
|
|
|
|
case PROP_FONT_NAME:
|
|
e_html_editor_selection_set_font_name (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_string (value));
|
|
return;
|
|
|
|
case PROP_FONT_SIZE:
|
|
e_html_editor_selection_set_font_size (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_int (value));
|
|
return;
|
|
|
|
case PROP_ITALIC:
|
|
e_html_editor_selection_set_italic (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_MONOSPACED:
|
|
e_html_editor_selection_set_monospaced (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_STRIKETHROUGH:
|
|
e_html_editor_selection_set_strikethrough (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_SUBSCRIPT:
|
|
e_html_editor_selection_set_subscript (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_SUPERSCRIPT:
|
|
e_html_editor_selection_set_superscript (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_UNDERLINE:
|
|
e_html_editor_selection_set_underline (
|
|
E_HTML_EDITOR_SELECTION (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_dispose (GObject *object)
|
|
{
|
|
EHTMLEditorSelectionPrivate *priv;
|
|
EHTMLEditorView *view;
|
|
|
|
priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (object);
|
|
|
|
view = g_weak_ref_get (&priv->html_editor_view);
|
|
if (view != NULL) {
|
|
g_signal_handler_disconnect (
|
|
view, priv->selection_changed_handler_id);
|
|
priv->selection_changed_handler_id = 0;
|
|
g_object_unref (view);
|
|
}
|
|
|
|
g_weak_ref_set (&priv->html_editor_view, NULL);
|
|
|
|
/* Chain up to parent's dispose() method. */
|
|
G_OBJECT_CLASS (e_html_editor_selection_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_finalize (GObject *object)
|
|
{
|
|
EHTMLEditorSelection *selection = E_HTML_EDITOR_SELECTION (object);
|
|
|
|
g_free (selection->priv->text);
|
|
g_free (selection->priv->background_color);
|
|
g_free (selection->priv->font_color);
|
|
g_free (selection->priv->font_family);
|
|
|
|
/* Chain up to parent's finalize() method. */
|
|
G_OBJECT_CLASS (e_html_editor_selection_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
e_html_editor_selection_class_init (EHTMLEditorSelectionClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
g_type_class_add_private (class, sizeof (EHTMLEditorSelectionPrivate));
|
|
|
|
object_class = G_OBJECT_CLASS (class);
|
|
object_class->get_property = html_editor_selection_get_property;
|
|
object_class->set_property = html_editor_selection_set_property;
|
|
object_class->dispose = html_editor_selection_dispose;
|
|
object_class->finalize = html_editor_selection_finalize;
|
|
|
|
/**
|
|
* EHTMLEditorSelection:alignment
|
|
*
|
|
* Holds alignment of current paragraph.
|
|
*/
|
|
/* FIXME: Convert the enum to a proper type */
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_ALIGNMENT,
|
|
g_param_spec_int (
|
|
"alignment",
|
|
NULL,
|
|
NULL,
|
|
E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
|
|
E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT,
|
|
E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
|
|
G_PARAM_READWRITE));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:background-color
|
|
*
|
|
* Holds background color of current selection or at current cursor
|
|
* position.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_BACKGROUND_COLOR,
|
|
g_param_spec_string (
|
|
"background-color",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:block-format
|
|
*
|
|
* Holds block format of current paragraph. See
|
|
* #EHTMLEditorSelectionBlockFormat for valid values.
|
|
*/
|
|
/* FIXME Convert the EHTMLEditorSelectionBlockFormat
|
|
* enum to a proper type. */
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_BLOCK_FORMAT,
|
|
g_param_spec_int (
|
|
"block-format",
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
G_MAXINT,
|
|
0,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:bold
|
|
*
|
|
* Holds whether current selection or text at current cursor position
|
|
* is bold.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_BOLD,
|
|
g_param_spec_boolean (
|
|
"bold",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_HTML_EDITOR_VIEW,
|
|
g_param_spec_object (
|
|
"html-editor-view",
|
|
NULL,
|
|
NULL,
|
|
E_TYPE_HTML_EDITOR_VIEW,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:font-color
|
|
*
|
|
* Holds font color of current selection or at current cursor position.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_FONT_COLOR,
|
|
g_param_spec_boxed (
|
|
"font-color",
|
|
NULL,
|
|
NULL,
|
|
GDK_TYPE_RGBA,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:font-name
|
|
*
|
|
* Holds name of font in current selection or at current cursor
|
|
* position.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_FONT_NAME,
|
|
g_param_spec_string (
|
|
"font-name",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:font-size
|
|
*
|
|
* Holds point size of current selection or at current cursor position.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_FONT_SIZE,
|
|
g_param_spec_int (
|
|
"font-size",
|
|
NULL,
|
|
NULL,
|
|
1,
|
|
7,
|
|
3,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:indented
|
|
*
|
|
* Holds whether current paragraph is indented. This does not include
|
|
* citations.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_INDENTED,
|
|
g_param_spec_boolean (
|
|
"indented",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:italic
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is italic.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_ITALIC,
|
|
g_param_spec_boolean (
|
|
"italic",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:monospaced
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is monospaced.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_MONOSPACED,
|
|
g_param_spec_boolean (
|
|
"monospaced",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:strikethrough
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is strikethrough.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_STRIKETHROUGH,
|
|
g_param_spec_boolean (
|
|
"strikethrough",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:superscript
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is in superscript.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_SUPERSCRIPT,
|
|
g_param_spec_boolean (
|
|
"superscript",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:subscript
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is in subscript.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_SUBSCRIPT,
|
|
g_param_spec_boolean (
|
|
"subscript",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:text
|
|
*
|
|
* Holds always up-to-date text of current selection.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_TEXT,
|
|
g_param_spec_string (
|
|
"text",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* EHTMLEditorSelection:underline
|
|
*
|
|
* Holds whether current selection or letter at current cursor position
|
|
* is underlined.
|
|
*/
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_UNDERLINE,
|
|
g_param_spec_boolean (
|
|
"underline",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static void
|
|
e_html_editor_selection_init (EHTMLEditorSelection *selection)
|
|
{
|
|
GSettings *g_settings;
|
|
|
|
selection->priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (selection);
|
|
|
|
g_settings = e_util_ref_settings ("org.gnome.evolution.mail");
|
|
selection->priv->word_wrap_length =
|
|
g_settings_get_int (g_settings, "composer-word-wrap-length");
|
|
g_object_unref (g_settings);
|
|
|
|
selection->priv->selection_changed_callbacks_blocked = FALSE;
|
|
}
|
|
|
|
gint
|
|
e_html_editor_selection_get_word_wrap_length (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), 72);
|
|
|
|
return selection->priv->word_wrap_length;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_ref_html_editor_view:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns a new reference to @selection's #EHTMLEditorView. Unreference
|
|
* the #EHTMLEditorView with g_object_unref() when finished with it.
|
|
*
|
|
* Returns: an #EHTMLEditorView
|
|
**/
|
|
EHTMLEditorView *
|
|
e_html_editor_selection_ref_html_editor_view (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
|
|
return g_weak_ref_get (&selection->priv->html_editor_view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_has_text:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection contains any text.
|
|
*
|
|
* Returns: @TRUE when current selection contains text, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_has_text (EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMRange *range;
|
|
WebKitDOMNode *node;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
|
|
node = webkit_dom_range_get_start_container (range, NULL);
|
|
if (WEBKIT_DOM_IS_TEXT (node)) {
|
|
g_object_unref (range);
|
|
return TRUE;
|
|
}
|
|
|
|
node = webkit_dom_range_get_end_container (range, NULL);
|
|
if (WEBKIT_DOM_IS_TEXT (node)) {
|
|
g_object_unref (range);
|
|
return TRUE;
|
|
}
|
|
|
|
node = WEBKIT_DOM_NODE (webkit_dom_range_clone_contents (range, NULL));
|
|
while (node) {
|
|
if (WEBKIT_DOM_IS_TEXT (node)) {
|
|
g_object_unref (range);
|
|
return TRUE;
|
|
}
|
|
|
|
if (webkit_dom_node_has_child_nodes (node)) {
|
|
node = webkit_dom_node_get_first_child (node);
|
|
} else if (webkit_dom_node_get_next_sibling (node)) {
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
} else {
|
|
node = webkit_dom_node_get_parent_node (node);
|
|
if (node) {
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (node)
|
|
g_object_unref (node);
|
|
|
|
g_object_unref (range);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_caret_word:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns word under cursor.
|
|
*
|
|
* Returns: A newly allocated string with current caret word or @NULL when there
|
|
* is no text under cursor or when selection is active. [transfer-full].
|
|
*/
|
|
gchar *
|
|
e_html_editor_selection_get_caret_word (EHTMLEditorSelection *selection)
|
|
{
|
|
gchar *word;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
|
|
/* Don't operate on the visible selection */
|
|
range = webkit_dom_range_clone_range (range, NULL);
|
|
webkit_dom_range_expand (range, "word", NULL);
|
|
word = webkit_dom_range_to_string (range, NULL);
|
|
|
|
g_object_unref (range);
|
|
|
|
return word;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_replace_caret_word:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @replacement: a string to replace current caret word with
|
|
*
|
|
* Replaces current word under cursor with @replacement.
|
|
*/
|
|
void
|
|
e_html_editor_selection_replace_caret_word (EHTMLEditorSelection *selection,
|
|
const gchar *replacement)
|
|
{
|
|
EHTMLEditorView *view;
|
|
WebKitWebView *web_view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (replacement != NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
web_view = WEBKIT_WEB_VIEW (view);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
document = webkit_web_view_get_dom_document (web_view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
webkit_dom_range_expand (range, "word", NULL);
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
|
|
e_html_editor_view_exec_command (
|
|
view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, replacement);
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_collapsed:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns if selection is collapsed.
|
|
*
|
|
* Returns: Whether the selection is collapsed (just caret) or not (someting is selected).
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_collapsed (EHTMLEditorSelection *selection)
|
|
{
|
|
gboolean collapsed;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), TRUE);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return TRUE;
|
|
|
|
collapsed = webkit_dom_range_get_collapsed (range, NULL);
|
|
g_object_unref (range);
|
|
|
|
return collapsed;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_string:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns currently selected string.
|
|
*
|
|
* Returns: A pointer to content of current selection. The string is owned by
|
|
* #EHTMLEditorSelection and should not be free'd.
|
|
*/
|
|
const gchar *
|
|
e_html_editor_selection_get_string (EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return NULL;
|
|
|
|
g_free (selection->priv->text);
|
|
selection->priv->text = webkit_dom_range_get_text (range);
|
|
|
|
g_object_unref (range);
|
|
|
|
return selection->priv->text;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_replace:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @new_string: a string to replace current selection with
|
|
*
|
|
* Replaces currently selected text with @new_string.
|
|
*/
|
|
void
|
|
e_html_editor_selection_replace (EHTMLEditorSelection *selection,
|
|
const gchar *new_string)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range;
|
|
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_REPLACE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
ev->data.string.from = webkit_dom_range_get_text (range);
|
|
ev->data.string.to = g_strdup (new_string);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
g_object_unref (range);
|
|
}
|
|
|
|
e_html_editor_view_exec_command (
|
|
view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, new_string);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_list_alignment_from_node:
|
|
* @node: #an WebKitDOMNode
|
|
*
|
|
* Returns alignment of given list.
|
|
*
|
|
* Returns: #EHTMLEditorSelectionAlignment
|
|
*/
|
|
EHTMLEditorSelectionAlignment
|
|
e_html_editor_selection_get_list_alignment_from_node (WebKitDOMNode *node)
|
|
{
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-left"))
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-center"))
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-right"))
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
|
|
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
}
|
|
|
|
static EHTMLEditorSelectionAlignment
|
|
e_html_editor_selection_get_alignment_from_node (WebKitDOMNode *node)
|
|
{
|
|
EHTMLEditorSelectionAlignment alignment;
|
|
gchar *value;
|
|
WebKitDOMCSSStyleDeclaration *style;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
|
|
document = webkit_dom_node_get_owner_document (node);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
|
|
style = webkit_dom_dom_window_get_computed_style (
|
|
dom_window, WEBKIT_DOM_ELEMENT (node), NULL);
|
|
value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
|
|
|
|
if (!value || !*value ||
|
|
(g_ascii_strncasecmp (value, "left", 4) == 0)) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
} else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
|
|
} else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
|
|
} else {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
}
|
|
|
|
g_object_unref (dom_window);
|
|
g_object_unref (style);
|
|
|
|
g_free (value);
|
|
|
|
return alignment;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_alignment:
|
|
* @selection: #an EHTMLEditorSelection
|
|
*
|
|
* Returns alignment of current paragraph
|
|
*
|
|
* Returns: #EHTMLEditorSelectionAlignment
|
|
*/
|
|
EHTMLEditorSelectionAlignment
|
|
e_html_editor_selection_get_alignment (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorSelectionAlignment alignment;
|
|
EHTMLEditorView *view;
|
|
gchar *value;
|
|
WebKitDOMCSSStyleDeclaration *style;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMElement *element;
|
|
WebKitDOMNode *node;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (
|
|
E_IS_HTML_EDITOR_SELECTION (selection),
|
|
E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_val_if_fail (view != NULL, FALSE);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
|
|
node = webkit_dom_range_get_start_container (range, NULL);
|
|
g_object_unref (range);
|
|
if (!node)
|
|
return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
|
|
if (WEBKIT_DOM_IS_ELEMENT (node))
|
|
element = WEBKIT_DOM_ELEMENT (node);
|
|
else
|
|
element = webkit_dom_node_get_parent_element (node);
|
|
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
style = webkit_dom_dom_window_get_computed_style (dom_window, element, NULL);
|
|
value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
|
|
|
|
if (!value || !*value ||
|
|
(g_ascii_strncasecmp (value, "left", 4) == 0)) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
} else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
|
|
} else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
|
|
} else {
|
|
alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
|
|
}
|
|
|
|
g_object_unref (style);
|
|
g_object_unref (dom_window);
|
|
g_free (value);
|
|
|
|
return alignment;
|
|
}
|
|
|
|
static void
|
|
set_ordered_list_type_to_element (WebKitDOMElement *list,
|
|
EHTMLEditorSelectionBlockFormat format)
|
|
{
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST)
|
|
webkit_dom_element_remove_attribute (list, "type");
|
|
else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
|
|
webkit_dom_element_set_attribute (list, "type", "A", NULL);
|
|
else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN)
|
|
webkit_dom_element_set_attribute (list, "type", "I", NULL);
|
|
}
|
|
|
|
static WebKitDOMElement *
|
|
create_list_element (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
gint level,
|
|
gboolean html_mode)
|
|
{
|
|
WebKitDOMElement *list;
|
|
gint offset = -SPACES_PER_LIST_LEVEL;
|
|
gboolean inserting_unordered_list =
|
|
format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
|
|
|
|
list = webkit_dom_document_create_element (
|
|
document, inserting_unordered_list ? "UL" : "OL", NULL);
|
|
|
|
set_ordered_list_type_to_element (list, format);
|
|
|
|
if (level >= 0)
|
|
offset = (level + 1) * -SPACES_PER_LIST_LEVEL;
|
|
|
|
if (!html_mode)
|
|
e_html_editor_selection_set_paragraph_style (
|
|
selection, list, -1, offset, "");
|
|
|
|
return list;
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
get_list_item_node_from_child (WebKitDOMNode *child)
|
|
{
|
|
WebKitDOMNode *parent = webkit_dom_node_get_parent_node (child);
|
|
|
|
while (parent && !WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent))
|
|
parent = webkit_dom_node_get_parent_node (parent);
|
|
|
|
return parent;
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
get_list_node_from_child (WebKitDOMNode *child)
|
|
{
|
|
WebKitDOMNode *parent = get_list_item_node_from_child (child);
|
|
|
|
return webkit_dom_node_get_parent_node (parent);
|
|
}
|
|
|
|
static void
|
|
format_change_list_from_list (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
EHTMLEditorSelectionBlockFormat to,
|
|
gboolean html_mode)
|
|
{
|
|
gboolean after_selection_end = FALSE;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list;
|
|
WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
if (!selection_start_marker || !selection_end_marker)
|
|
return;
|
|
|
|
new_list = create_list_element (selection, document, to, 0, html_mode);
|
|
|
|
/* Copy elements from previous block to list */
|
|
item = get_list_item_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
source_list = webkit_dom_node_get_parent_node (item);
|
|
current_list = source_list;
|
|
source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
|
|
element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
|
|
|
|
while (item) {
|
|
WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
|
|
|
|
if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
|
|
webkit_dom_node_append_child (
|
|
after_selection_end ?
|
|
source_list_clone : WEBKIT_DOM_NODE (new_list),
|
|
WEBKIT_DOM_NODE (item),
|
|
NULL);
|
|
}
|
|
|
|
if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker))) {
|
|
g_object_unref (source_list_clone);
|
|
source_list_clone = webkit_dom_node_clone_node (current_list, FALSE);
|
|
after_selection_end = TRUE;
|
|
}
|
|
|
|
if (!next_item) {
|
|
if (after_selection_end)
|
|
break;
|
|
current_list = webkit_dom_node_get_next_sibling (current_list);
|
|
next_item = webkit_dom_node_get_first_child (current_list);
|
|
}
|
|
item = next_item;
|
|
}
|
|
|
|
if (webkit_dom_node_has_child_nodes (source_list_clone))
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (source_list),
|
|
WEBKIT_DOM_NODE (source_list_clone),
|
|
webkit_dom_node_get_next_sibling (source_list), NULL);
|
|
if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list)))
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (source_list),
|
|
WEBKIT_DOM_NODE (new_list),
|
|
webkit_dom_node_get_next_sibling (source_list), NULL);
|
|
if (!webkit_dom_node_has_child_nodes (source_list))
|
|
remove_node (source_list);
|
|
}
|
|
|
|
static gboolean
|
|
node_is_list_or_item (WebKitDOMNode *node)
|
|
{
|
|
return node && (
|
|
WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node) ||
|
|
WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node) ||
|
|
WEBKIT_DOM_IS_HTMLLI_ELEMENT (node));
|
|
}
|
|
|
|
static gboolean
|
|
node_is_list (WebKitDOMNode *node)
|
|
{
|
|
return node && (
|
|
WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node) ||
|
|
WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node));
|
|
}
|
|
|
|
static void
|
|
set_block_alignment (WebKitDOMElement *element,
|
|
const gchar *class)
|
|
{
|
|
WebKitDOMElement *parent;
|
|
|
|
element_remove_class (element, "-x-evo-align-center");
|
|
element_remove_class (element, "-x-evo-align-right");
|
|
element_add_class (element, class);
|
|
parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
|
|
element_remove_class (parent, "-x-evo-align-center");
|
|
element_remove_class (parent, "-x-evo-align-right");
|
|
parent = webkit_dom_node_get_parent_element (
|
|
WEBKIT_DOM_NODE (parent));
|
|
}
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
get_parent_block_node_from_child (WebKitDOMNode *node)
|
|
{
|
|
WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-temp-text-wrapper") ||
|
|
element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") ||
|
|
element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character") ||
|
|
element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-signature") ||
|
|
WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) ||
|
|
element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") ||
|
|
element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") ||
|
|
element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u"))
|
|
parent = webkit_dom_node_get_parent_node (parent);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted"))
|
|
parent = webkit_dom_node_get_parent_node (parent);
|
|
|
|
return parent;
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_get_selection_coordinates (EHTMLEditorSelection *selection,
|
|
guint *start_x,
|
|
guint *start_y,
|
|
guint *end_x,
|
|
guint *end_y)
|
|
{
|
|
EHTMLEditorView *view;
|
|
gboolean created_selection_markers = FALSE;
|
|
guint local_x = 0, local_y = 0;
|
|
WebKitDOMElement *element, *parent;
|
|
WebKitDOMDocument *document;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (start_x != NULL);
|
|
g_return_if_fail (start_y != NULL);
|
|
g_return_if_fail (end_x != NULL);
|
|
g_return_if_fail (end_y != NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
|
|
element = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (!element) {
|
|
created_selection_markers = TRUE;
|
|
e_html_editor_selection_save (selection);
|
|
element = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (!element)
|
|
return;
|
|
}
|
|
|
|
parent = element;
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
|
|
local_x += (guint) webkit_dom_element_get_offset_left (parent);
|
|
local_y += (guint) webkit_dom_element_get_offset_top (parent);
|
|
parent = webkit_dom_element_get_offset_parent (parent);
|
|
}
|
|
|
|
if (start_x)
|
|
*start_x = local_x;
|
|
if (start_y)
|
|
*start_y = local_y;
|
|
|
|
if (e_html_editor_selection_is_collapsed (selection)) {
|
|
*end_x = local_x;
|
|
*end_y = local_y;
|
|
|
|
if (created_selection_markers)
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
return;
|
|
}
|
|
|
|
element = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
|
|
local_x = 0;
|
|
local_y = 0;
|
|
|
|
parent = element;
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
|
|
local_x += (guint) webkit_dom_element_get_offset_left (parent);
|
|
local_y += (guint) webkit_dom_element_get_offset_top (parent);
|
|
parent = webkit_dom_element_get_offset_parent (parent);
|
|
}
|
|
|
|
if (end_x)
|
|
*end_x = local_x;
|
|
if (end_y)
|
|
*end_y = local_y;
|
|
|
|
if (created_selection_markers)
|
|
e_html_editor_selection_restore (selection);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_alignment:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @alignment: an #EHTMLEditorSelectionAlignment value to apply
|
|
*
|
|
* Sets alignment of current paragraph to give @alignment.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionAlignment alignment)
|
|
{
|
|
EHTMLEditorSelectionAlignment current_alignment;
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
gboolean after_selection_end = FALSE;
|
|
const gchar *class = "", *list_class = "";
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
current_alignment = e_html_editor_selection_get_alignment (selection);
|
|
|
|
if (current_alignment == alignment)
|
|
return;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
switch (alignment) {
|
|
case E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER:
|
|
class = "-x-evo-align-center";
|
|
list_class = "-x-evo-list-item-align-center";
|
|
break;
|
|
|
|
case E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT:
|
|
break;
|
|
|
|
case E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT:
|
|
class = "-x-evo-align-right";
|
|
list_class = "-x-evo-list-item-align-right";
|
|
break;
|
|
}
|
|
|
|
selection->priv->alignment = alignment;
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
if (!selection_start_marker) {
|
|
g_object_unref (view);
|
|
return;
|
|
}
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_ALIGNMENT;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = current_alignment;
|
|
ev->data.style.to = alignment;
|
|
}
|
|
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
while (block && !after_selection_end) {
|
|
WebKitDOMNode *next_block;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
if (node_is_list (block)) {
|
|
WebKitDOMNode *item = webkit_dom_node_get_first_child (block);
|
|
|
|
while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
|
|
element_remove_class (
|
|
WEBKIT_DOM_ELEMENT (item),
|
|
"-x-evo-list-item-align-center");
|
|
element_remove_class (
|
|
WEBKIT_DOM_ELEMENT (item),
|
|
"-x-evo-list-item-align-right");
|
|
|
|
element_add_class (WEBKIT_DOM_ELEMENT (item), list_class);
|
|
after_selection_end = webkit_dom_node_contains (
|
|
item, WEBKIT_DOM_NODE (selection_end_marker));
|
|
if (after_selection_end)
|
|
break;
|
|
item = webkit_dom_node_get_next_sibling (item);
|
|
}
|
|
} else {
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-indented")) {
|
|
gint ii, length;
|
|
WebKitDOMNodeList *list;
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
WEBKIT_DOM_ELEMENT (block),
|
|
".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
|
|
NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *item = webkit_dom_node_list_item (list, ii);
|
|
|
|
set_block_alignment (WEBKIT_DOM_ELEMENT (item), class);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
item, WEBKIT_DOM_NODE (selection_end_marker));
|
|
g_object_unref (item);
|
|
if (after_selection_end)
|
|
break;
|
|
}
|
|
|
|
g_object_unref (list);
|
|
} else {
|
|
set_block_alignment (WEBKIT_DOM_ELEMENT (block), class);
|
|
}
|
|
}
|
|
|
|
block = next_block;
|
|
}
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "alignment");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_background_color:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns background color of currently selected text or letter at current
|
|
* cursor position.
|
|
*
|
|
* Returns: A string with code of current background color.
|
|
*/
|
|
const gchar *
|
|
e_html_editor_selection_get_background_color (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
WebKitDOMNode *ancestor;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMCSSStyleDeclaration *css;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_val_if_fail (view != NULL, FALSE);
|
|
|
|
if (!e_html_editor_view_get_html_mode (view)) {
|
|
g_object_unref (view);
|
|
return "#ffffff";
|
|
}
|
|
|
|
g_object_unref (view);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
|
|
ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
|
|
css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor));
|
|
g_free (selection->priv->background_color);
|
|
selection->priv->background_color =
|
|
webkit_dom_css_style_declaration_get_property_value (
|
|
css, "background-color");
|
|
|
|
g_object_unref (css);
|
|
g_object_unref (range);
|
|
|
|
return selection->priv->background_color;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_background_color:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @color: code of new background color to set
|
|
*
|
|
* Changes background color of current selection or letter at current cursor
|
|
* position to @color.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_background_color (EHTMLEditorSelection *selection,
|
|
const gchar *color)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (color != NULL && *color != '\0');
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR;
|
|
e_html_editor_view_exec_command (view, command, color);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "background-color");
|
|
}
|
|
|
|
static gint
|
|
get_indentation_level (WebKitDOMElement *element)
|
|
{
|
|
WebKitDOMElement *parent;
|
|
gint level = 0;
|
|
|
|
if (element_has_class (element, "-x-evo-indented"))
|
|
level++;
|
|
|
|
parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
|
|
/* Count level of indentation */
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
|
|
if (element_has_class (parent, "-x-evo-indented"))
|
|
level++;
|
|
|
|
parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
get_block_node (WebKitDOMRange *range)
|
|
{
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
node = get_parent_block_node_from_child (node);
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_list_format_from_node:
|
|
* @node: an #WebKitDOMNode
|
|
*
|
|
* Returns block format of given list.
|
|
*
|
|
* Returns: #EHTMLEditorSelectionBlockFormat
|
|
*/
|
|
EHTMLEditorSelectionBlockFormat
|
|
e_html_editor_selection_get_list_format_from_node (WebKitDOMNode *node)
|
|
{
|
|
EHTMLEditorSelectionBlockFormat format =
|
|
E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
|
|
|
|
if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
|
|
return -1;
|
|
|
|
if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node))
|
|
return format;
|
|
|
|
if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node)) {
|
|
gchar *type_value = webkit_dom_element_get_attribute (
|
|
WEBKIT_DOM_ELEMENT (node), "type");
|
|
|
|
if (!type_value)
|
|
return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
|
|
|
|
if (!*type_value)
|
|
format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
|
|
else if (g_ascii_strcasecmp (type_value, "A") == 0)
|
|
format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
|
|
else if (g_ascii_strcasecmp (type_value, "I") == 0)
|
|
format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
|
|
g_free (type_value);
|
|
|
|
return format;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_block_format:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns block format of current paragraph.
|
|
*
|
|
* Returns: #EHTMLEditorSelectionBlockFormat
|
|
*/
|
|
EHTMLEditorSelectionBlockFormat
|
|
e_html_editor_selection_get_block_format (EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMNode *node;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMElement *element;
|
|
EHTMLEditorSelectionBlockFormat result;
|
|
|
|
g_return_val_if_fail (
|
|
E_IS_HTML_EDITOR_SELECTION (selection),
|
|
E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
|
|
node = webkit_dom_range_get_start_container (range, NULL);
|
|
|
|
if (e_html_editor_dom_node_find_parent_element (node, "UL")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
|
|
} else if ((element = e_html_editor_dom_node_find_parent_element (node, "OL")) != NULL) {
|
|
result = e_html_editor_selection_get_list_format_from_node (WEBKIT_DOM_NODE (element));
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "PRE")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "ADDRESS")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H1")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H2")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H3")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H4")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H5")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5;
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "H6")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6;
|
|
} else if ((element = e_html_editor_dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) {
|
|
if (element_has_class (element, "-x-evo-indented"))
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
else {
|
|
WebKitDOMNode *block = get_block_node (range);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph"))
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
else {
|
|
/* Paragraphs inside quote */
|
|
if ((element = e_html_editor_dom_node_find_parent_element (node, "DIV")) != NULL)
|
|
if (element_has_class (element, "-x-evo-paragraph"))
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
else
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE;
|
|
else
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE;
|
|
}
|
|
}
|
|
} else if (e_html_editor_dom_node_find_parent_element (node, "P")) {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
} else {
|
|
result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
|
|
}
|
|
|
|
g_object_unref (range);
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
is_selection_position_node (WebKitDOMNode *node)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
if (!node || !WEBKIT_DOM_IS_ELEMENT (node))
|
|
return FALSE;
|
|
|
|
element = WEBKIT_DOM_ELEMENT (node);
|
|
|
|
return element_has_id (element, "-x-evo-selection-start-marker") ||
|
|
element_has_id (element, "-x-evo-selection-end-marker");
|
|
}
|
|
|
|
static void
|
|
merge_list_into_list (WebKitDOMNode *from,
|
|
WebKitDOMNode *to,
|
|
gboolean insert_before)
|
|
{
|
|
WebKitDOMNode *item;
|
|
|
|
if (!(to && from))
|
|
return;
|
|
|
|
while ((item = webkit_dom_node_get_first_child (from)) != NULL) {
|
|
if (insert_before)
|
|
webkit_dom_node_insert_before (
|
|
to, item, webkit_dom_node_get_last_child (to), NULL);
|
|
else
|
|
webkit_dom_node_append_child (to, item, NULL);
|
|
}
|
|
|
|
if (!webkit_dom_node_has_child_nodes (from))
|
|
remove_node (from);
|
|
|
|
}
|
|
|
|
static void
|
|
merge_lists_if_possible (WebKitDOMNode *list)
|
|
{
|
|
EHTMLEditorSelectionBlockFormat format, prev, next;
|
|
WebKitDOMNode *prev_sibling, *next_sibling;
|
|
|
|
prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list));
|
|
next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list));
|
|
|
|
format = e_html_editor_selection_get_list_format_from_node (list),
|
|
prev = e_html_editor_selection_get_list_format_from_node (prev_sibling);
|
|
next = e_html_editor_selection_get_list_format_from_node (next_sibling);
|
|
|
|
if (format == prev && format != -1 && prev != -1)
|
|
merge_list_into_list (prev_sibling, list, TRUE);
|
|
|
|
if (format == next && format != -1 && next != -1)
|
|
merge_list_into_list (next_sibling, list, FALSE);
|
|
}
|
|
|
|
void
|
|
remove_wrapping_from_element (WebKitDOMElement *element)
|
|
{
|
|
WebKitDOMNodeList *list;
|
|
gint ii, length;
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
element, "br.-x-evo-wrap-br", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
|
|
WebKitDOMNode *parent;
|
|
|
|
parent = get_parent_block_node_from_child (node);
|
|
if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped"))
|
|
remove_node (node);
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
element, "span[data-hidden-space]", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *hidden_space_node;
|
|
WebKitDOMNode *parent;
|
|
|
|
hidden_space_node = webkit_dom_node_list_item (list, ii);
|
|
parent = get_parent_block_node_from_child (hidden_space_node);
|
|
if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) {
|
|
webkit_dom_html_element_set_outer_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL);
|
|
}
|
|
g_object_unref (hidden_space_node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
|
|
}
|
|
|
|
void
|
|
remove_quoting_from_element (WebKitDOMElement *element)
|
|
{
|
|
gint ii, length;
|
|
WebKitDOMNodeList *list;
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
element, "span.-x-evo-quoted", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
|
|
remove_node (node);
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
element, "span.-x-evo-temp-text-wrapper", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
|
|
WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
|
|
|
|
while (webkit_dom_node_get_first_child (node))
|
|
webkit_dom_node_insert_before (
|
|
parent,
|
|
webkit_dom_node_get_first_child (node),
|
|
node,
|
|
NULL);
|
|
|
|
remove_node (node);
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
element, "br.-x-evo-temp-br", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
|
|
remove_node (node);
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
|
|
}
|
|
|
|
static gint
|
|
get_citation_level (WebKitDOMNode *node)
|
|
{
|
|
WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
|
|
gint level = 0;
|
|
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
|
|
if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
|
|
webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
|
|
level++;
|
|
|
|
parent = webkit_dom_node_get_parent_node (parent);
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
static gboolean
|
|
is_citation_node (WebKitDOMNode *node)
|
|
{
|
|
gchar *value;
|
|
|
|
if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
|
|
return FALSE;
|
|
|
|
value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
|
|
|
|
/* citation == <blockquote type='cite'> */
|
|
if (g_strcmp0 (value, "cite") == 0) {
|
|
g_free (value);
|
|
return TRUE;
|
|
} else {
|
|
g_free (value);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
process_block_to_block (EHTMLEditorSelection *selection,
|
|
EHTMLEditorView *view,
|
|
WebKitDOMDocument *document,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
const gchar *value,
|
|
WebKitDOMNode *block,
|
|
WebKitDOMNode *end_block,
|
|
WebKitDOMNode *blockquote,
|
|
gboolean html_mode)
|
|
{
|
|
gboolean after_selection_end = FALSE;
|
|
WebKitDOMNode *next_block;
|
|
|
|
while (!after_selection_end && block) {
|
|
gboolean quoted = FALSE;
|
|
gboolean empty = FALSE;
|
|
gchar *content;
|
|
WebKitDOMNode *child;
|
|
WebKitDOMElement *element;
|
|
|
|
if (is_citation_node (block)) {
|
|
gboolean finished;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
finished = process_block_to_block (
|
|
selection,
|
|
view,
|
|
document,
|
|
format,
|
|
value,
|
|
webkit_dom_node_get_first_child (block),
|
|
end_block,
|
|
blockquote,
|
|
html_mode);
|
|
|
|
if (finished)
|
|
return TRUE;
|
|
|
|
block = next_block;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
|
|
quoted = TRUE;
|
|
remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
}
|
|
|
|
if (!html_mode)
|
|
remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
|
|
after_selection_end = webkit_dom_node_is_same_node (block, end_block);
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH ||
|
|
format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)
|
|
element = e_html_editor_selection_get_paragraph_element (
|
|
selection, document, -1, 0);
|
|
else
|
|
element = webkit_dom_document_create_element (
|
|
document, value, NULL);
|
|
|
|
content = webkit_dom_node_get_text_content (block);
|
|
|
|
empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
|
|
g_free (content);
|
|
|
|
while ((child = webkit_dom_node_get_first_child (block))) {
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child))
|
|
empty = FALSE;
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element), child, NULL);
|
|
}
|
|
|
|
if (empty) {
|
|
WebKitDOMElement *br;
|
|
|
|
br = webkit_dom_document_create_element (
|
|
document, "BR", NULL);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (br), NULL);
|
|
}
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (block),
|
|
WEBKIT_DOM_NODE (element),
|
|
block,
|
|
NULL);
|
|
|
|
remove_node (block);
|
|
|
|
if (!next_block && !after_selection_end) {
|
|
gint citation_level;
|
|
|
|
citation_level = get_citation_level (WEBKIT_DOM_NODE (element));
|
|
|
|
if (citation_level > 0) {
|
|
next_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
|
|
next_block = webkit_dom_node_get_next_sibling (next_block);
|
|
}
|
|
}
|
|
|
|
block = next_block;
|
|
|
|
if (!html_mode &&
|
|
(format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH ||
|
|
format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)) {
|
|
gint citation_level, quote;
|
|
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)
|
|
citation_level = 1;
|
|
else
|
|
citation_level = get_citation_level (WEBKIT_DOM_NODE (element));
|
|
quote = citation_level ? citation_level * 2 : 0;
|
|
|
|
if (citation_level > 0)
|
|
element = e_html_editor_selection_wrap_paragraph_length (
|
|
selection, element, selection->priv->word_wrap_length - quote);
|
|
}
|
|
|
|
if (blockquote && format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) {
|
|
webkit_dom_node_append_child (
|
|
blockquote, WEBKIT_DOM_NODE (element), NULL);
|
|
if (!html_mode)
|
|
e_html_editor_view_quote_plain_text_element_after_wrapping (document, element, 1);
|
|
} else
|
|
if (!html_mode && quoted)
|
|
e_html_editor_view_quote_plain_text_element (view, element);
|
|
}
|
|
|
|
return after_selection_end;
|
|
}
|
|
|
|
static WebKitDOMElement *
|
|
create_selection_marker (WebKitDOMDocument *document,
|
|
gboolean start)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
element = webkit_dom_document_create_element (
|
|
document, "SPAN", NULL);
|
|
webkit_dom_element_set_id (
|
|
element,
|
|
start ? "-x-evo-selection-start-marker" :
|
|
"-x-evo-selection-end-marker");
|
|
|
|
return element;
|
|
}
|
|
|
|
static void
|
|
remove_selection_markers (WebKitDOMDocument *document)
|
|
{
|
|
WebKitDOMElement *marker;
|
|
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (marker)
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
if (marker)
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
}
|
|
|
|
static void
|
|
add_selection_markers_into_element_start (WebKitDOMDocument *document,
|
|
WebKitDOMElement *element,
|
|
WebKitDOMElement **selection_start_marker,
|
|
WebKitDOMElement **selection_end_marker)
|
|
{
|
|
WebKitDOMElement *marker;
|
|
|
|
remove_selection_markers (document);
|
|
marker = create_selection_marker (document, FALSE);
|
|
webkit_dom_node_insert_before (
|
|
WEBKIT_DOM_NODE (element),
|
|
WEBKIT_DOM_NODE (marker),
|
|
webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
|
|
NULL);
|
|
if (selection_end_marker)
|
|
*selection_end_marker = marker;
|
|
|
|
marker = create_selection_marker (document, TRUE);
|
|
webkit_dom_node_insert_before (
|
|
WEBKIT_DOM_NODE (element),
|
|
WEBKIT_DOM_NODE (marker),
|
|
webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
|
|
NULL);
|
|
if (selection_start_marker)
|
|
*selection_start_marker = marker;
|
|
}
|
|
|
|
static void
|
|
add_selection_markers_into_element_end (WebKitDOMDocument *document,
|
|
WebKitDOMElement *element,
|
|
WebKitDOMElement **selection_start_marker,
|
|
WebKitDOMElement **selection_end_marker)
|
|
{
|
|
WebKitDOMElement *marker;
|
|
|
|
remove_selection_markers (document);
|
|
marker = create_selection_marker (document, TRUE);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
|
|
if (selection_start_marker)
|
|
*selection_start_marker = marker;
|
|
|
|
marker = create_selection_marker (document, FALSE);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
|
|
if (selection_end_marker)
|
|
*selection_end_marker = marker;
|
|
}
|
|
|
|
static void
|
|
format_change_block_to_block (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
EHTMLEditorView *view,
|
|
const gchar *value,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
gboolean html_mode;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block, *end_block, *blockquote = NULL;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* If the selection was not saved, move it into the first child of body */
|
|
if (!selection_start_marker || !selection_end_marker) {
|
|
WebKitDOMHTMLElement *body;
|
|
WebKitDOMNode *child;
|
|
|
|
body = webkit_dom_document_get_body (document);
|
|
child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
|
|
|
|
add_selection_markers_into_element_start (
|
|
document,
|
|
WEBKIT_DOM_ELEMENT (child),
|
|
&selection_start_marker,
|
|
&selection_end_marker);
|
|
}
|
|
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
html_mode = e_html_editor_view_get_html_mode (view);
|
|
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) {
|
|
blockquote = WEBKIT_DOM_NODE (
|
|
webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL));
|
|
|
|
webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (blockquote), "type", "cite", NULL);
|
|
if (!html_mode)
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (blockquote), "class", "-x-evo-plaintext-quoted", NULL);
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (block),
|
|
blockquote,
|
|
block,
|
|
NULL);
|
|
}
|
|
|
|
end_block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
/* Process all blocks that are in the selection one by one */
|
|
process_block_to_block (
|
|
selection, view, document, format, value, block, end_block, blockquote, html_mode);
|
|
}
|
|
|
|
static void
|
|
format_change_block_to_list (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
EHTMLEditorView *view,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
gboolean after_selection_end = FALSE, in_quote = FALSE;
|
|
gboolean html_mode = e_html_editor_view_get_html_mode (view);
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list;
|
|
WebKitDOMNode *block, *next_block;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* If the selection was not saved, move it into the first child of body */
|
|
if (!selection_start_marker || !selection_end_marker) {
|
|
WebKitDOMHTMLElement *body;
|
|
WebKitDOMNode *child;
|
|
|
|
body = webkit_dom_document_get_body (document);
|
|
child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
|
|
|
|
add_selection_markers_into_element_start (
|
|
document,
|
|
WEBKIT_DOM_ELEMENT (child),
|
|
&selection_start_marker,
|
|
&selection_end_marker);
|
|
}
|
|
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
list = create_list_element (selection, document, format, 0, html_mode);
|
|
|
|
if (webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
|
|
WebKitDOMElement *element;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range;
|
|
|
|
in_quote = TRUE;
|
|
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
range = webkit_dom_document_create_range (document);
|
|
|
|
webkit_dom_range_select_node (range, block, NULL);
|
|
webkit_dom_range_collapse (range, TRUE, NULL);
|
|
webkit_dom_dom_selection_remove_all_ranges (dom_selection);
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
|
|
e_html_editor_view_exec_command (
|
|
view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
|
|
|
|
element = webkit_dom_document_query_selector (
|
|
document, "body>br", NULL);
|
|
|
|
webkit_dom_node_replace_child (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
|
|
WEBKIT_DOM_NODE (list),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
} else
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (block),
|
|
WEBKIT_DOM_NODE (list),
|
|
block,
|
|
NULL);
|
|
|
|
/* Process all blocks that are in the selection one by one */
|
|
while (block && !after_selection_end) {
|
|
gboolean empty = FALSE;
|
|
gchar *content;
|
|
WebKitDOMNode *child, *parent;
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (
|
|
WEBKIT_DOM_NODE (block));
|
|
|
|
remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
|
|
item = webkit_dom_document_create_element (document, "LI", NULL);
|
|
content = webkit_dom_node_get_text_content (block);
|
|
|
|
empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
|
|
g_free (content);
|
|
|
|
while ((child = webkit_dom_node_get_first_child (block))) {
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child))
|
|
empty = FALSE;
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (item), child, NULL);
|
|
}
|
|
|
|
/* We have to use again the hidden space to move caret into newly inserted list */
|
|
if (empty) {
|
|
WebKitDOMElement *br;
|
|
|
|
br = webkit_dom_document_create_element (
|
|
document, "BR", NULL);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (item), WEBKIT_DOM_NODE (br), NULL);
|
|
}
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL);
|
|
|
|
parent = webkit_dom_node_get_parent_node (block);
|
|
remove_node (block);
|
|
|
|
if (in_quote) {
|
|
/* Remove all parents if previously removed node was the
|
|
* only one with text content */
|
|
content = webkit_dom_node_get_text_content (parent);
|
|
while (parent && content && !*content) {
|
|
WebKitDOMNode *tmp = webkit_dom_node_get_parent_node (parent);
|
|
|
|
remove_node (parent);
|
|
parent = tmp;
|
|
|
|
g_free (content);
|
|
content = webkit_dom_node_get_text_content (parent);
|
|
}
|
|
g_free (content);
|
|
}
|
|
|
|
block = next_block;
|
|
}
|
|
|
|
merge_lists_if_possible (WEBKIT_DOM_NODE (list));
|
|
}
|
|
|
|
static void
|
|
format_change_list_to_list (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
WebKitDOMDocument *document,
|
|
gboolean html_mode)
|
|
{
|
|
EHTMLEditorSelectionBlockFormat prev = 0, next = 0;
|
|
gboolean done = FALSE, indented = FALSE;
|
|
gboolean selection_starts_in_first_child, selection_ends_in_last_child;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *prev_list, *current_list, *next_list;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
current_list = get_list_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
prev_list = get_list_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
next_list = get_list_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
selection_starts_in_first_child =
|
|
webkit_dom_node_contains (
|
|
webkit_dom_node_get_first_child (current_list),
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
selection_ends_in_last_child =
|
|
webkit_dom_node_contains (
|
|
webkit_dom_node_get_last_child (current_list),
|
|
WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented");
|
|
|
|
if (!prev_list || !next_list || indented) {
|
|
format_change_list_from_list (selection, document, format, html_mode);
|
|
return;
|
|
}
|
|
|
|
if (webkit_dom_node_is_same_node (prev_list, next_list)) {
|
|
prev_list = webkit_dom_node_get_previous_sibling (
|
|
webkit_dom_node_get_parent_node (
|
|
webkit_dom_node_get_parent_node (
|
|
WEBKIT_DOM_NODE (selection_start_marker))));
|
|
next_list = webkit_dom_node_get_next_sibling (
|
|
webkit_dom_node_get_parent_node (
|
|
webkit_dom_node_get_parent_node (
|
|
WEBKIT_DOM_NODE (selection_end_marker))));
|
|
if (!prev_list || !next_list) {
|
|
format_change_list_from_list (selection, document, format, html_mode);
|
|
return;
|
|
}
|
|
}
|
|
|
|
prev = e_html_editor_selection_get_list_format_from_node (prev_list);
|
|
next = e_html_editor_selection_get_list_format_from_node (next_list);
|
|
|
|
if (format == prev && format != -1 && prev != -1) {
|
|
if (selection_starts_in_first_child && selection_ends_in_last_child) {
|
|
done = TRUE;
|
|
merge_list_into_list (current_list, prev_list, FALSE);
|
|
}
|
|
}
|
|
|
|
if (format == next && format != -1 && next != -1) {
|
|
if (selection_starts_in_first_child && selection_ends_in_last_child) {
|
|
done = TRUE;
|
|
merge_list_into_list (next_list, prev_list, FALSE);
|
|
}
|
|
}
|
|
|
|
if (done)
|
|
return;
|
|
|
|
format_change_list_from_list (selection, document, format, html_mode);
|
|
}
|
|
|
|
static void
|
|
format_change_list_to_block (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionBlockFormat format,
|
|
const gchar *value,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
gboolean after_end = FALSE;
|
|
WebKitDOMElement *selection_start, *element, *selection_end;
|
|
WebKitDOMNode *source_list, *next_item, *item, *source_list_clone;
|
|
|
|
selection_start = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
item = get_list_item_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start));
|
|
source_list = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (item));
|
|
source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (source_list),
|
|
WEBKIT_DOM_NODE (source_list_clone),
|
|
webkit_dom_node_get_next_sibling (source_list),
|
|
NULL);
|
|
|
|
next_item = item;
|
|
|
|
/* Process all nodes that are in selection one by one */
|
|
while (next_item) {
|
|
WebKitDOMNode *tmp;
|
|
|
|
tmp = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (next_item));
|
|
|
|
if (!after_end) {
|
|
WebKitDOMNode *node;
|
|
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH)
|
|
element = e_html_editor_selection_get_paragraph_element (
|
|
selection, document, -1, 0);
|
|
else
|
|
element = webkit_dom_document_create_element (
|
|
document, value, NULL);
|
|
|
|
after_end = webkit_dom_node_contains (next_item, WEBKIT_DOM_NODE (selection_end));
|
|
|
|
while ((node = webkit_dom_node_get_first_child (next_item)))
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element), node, NULL);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (source_list),
|
|
WEBKIT_DOM_NODE (element),
|
|
source_list_clone,
|
|
NULL);
|
|
|
|
remove_node (next_item);
|
|
|
|
next_item = tmp;
|
|
} else {
|
|
webkit_dom_node_append_child (
|
|
source_list_clone, next_item, NULL);
|
|
|
|
next_item = tmp;
|
|
}
|
|
}
|
|
|
|
remove_node_if_empty (source_list_clone);
|
|
remove_node_if_empty (source_list);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_block_format:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @format: an #EHTMLEditorSelectionBlockFormat value
|
|
*
|
|
* Changes block format of current paragraph to @format.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
|
|
EHTMLEditorSelectionBlockFormat format)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorSelectionBlockFormat current_format;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
const gchar *value;
|
|
gboolean from_list = FALSE, to_list = FALSE, html_mode;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
current_format = e_html_editor_selection_get_block_format (selection);
|
|
if (current_format == format) {
|
|
return;
|
|
}
|
|
|
|
switch (format) {
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE:
|
|
value = "BLOCKQUOTE";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1:
|
|
value = "H1";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2:
|
|
value = "H2";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3:
|
|
value = "H3";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4:
|
|
value = "H4";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5:
|
|
value = "H5";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6:
|
|
value = "H6";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH:
|
|
value = "P";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE:
|
|
value = "PRE";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS:
|
|
value = "ADDRESS";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST:
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
|
|
to_list = TRUE;
|
|
value = NULL;
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST:
|
|
to_list = TRUE;
|
|
value = NULL;
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE:
|
|
default:
|
|
value = NULL;
|
|
break;
|
|
}
|
|
|
|
/* H1 - H6 have bold font by default */
|
|
if (format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 &&
|
|
format <= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6)
|
|
selection->priv->is_bold = TRUE;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
html_mode = e_html_editor_view_get_html_mode (view);
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
from_list =
|
|
current_format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range) {
|
|
g_object_unref (view);
|
|
return;
|
|
}
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)
|
|
ev->type = HISTORY_BLOCK_FORMAT;
|
|
else
|
|
ev->type = HISTORY_BLOCKQUOTE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
|
|
if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) {
|
|
ev->data.style.from = current_format;
|
|
ev->data.style.to = format;
|
|
} else {
|
|
WebKitDOMDocumentFragment *fragment;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block, *end_block;
|
|
|
|
selection_start_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
selection_end_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
end_block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_end_marker));
|
|
if (webkit_dom_range_get_collapsed (range, NULL) ||
|
|
webkit_dom_node_is_same_node (block, end_block)) {
|
|
fragment = webkit_dom_document_create_document_fragment (document);
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (fragment),
|
|
webkit_dom_node_clone_node (block, TRUE),
|
|
NULL);
|
|
} else {
|
|
fragment = webkit_dom_range_clone_contents (range, NULL);
|
|
webkit_dom_node_replace_child (
|
|
WEBKIT_DOM_NODE (fragment),
|
|
webkit_dom_node_clone_node (block, TRUE),
|
|
webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
|
|
NULL);
|
|
|
|
webkit_dom_node_replace_child (
|
|
WEBKIT_DOM_NODE (fragment),
|
|
webkit_dom_node_clone_node (end_block, TRUE),
|
|
webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)),
|
|
NULL);
|
|
}
|
|
ev->data.fragment = fragment;
|
|
}
|
|
}
|
|
|
|
g_object_unref (range);
|
|
|
|
if (from_list && to_list)
|
|
format_change_list_to_list (selection, format, document, html_mode);
|
|
|
|
if (!from_list && !to_list)
|
|
format_change_block_to_block (selection, format, view, value, document);
|
|
|
|
if (from_list && !to_list) {
|
|
format_change_list_to_block (selection, format, value, document);
|
|
if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) {
|
|
e_html_editor_selection_restore (selection);
|
|
format_change_block_to_block (selection, format, view, value, document);
|
|
}
|
|
}
|
|
|
|
if (!from_list && to_list)
|
|
format_change_block_to_list (selection, format, view, document);
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
/* When changing the format we need to re-set the alignment */
|
|
e_html_editor_selection_set_alignment (selection, selection->priv->alignment);
|
|
|
|
e_html_editor_view_set_changed (view, TRUE);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "block-format");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_font_color:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @rgba: a #GdkRGBA object to be set to current font color
|
|
*
|
|
* Sets @rgba to contain color of current text selection or letter at current
|
|
* cursor position.
|
|
*/
|
|
void
|
|
e_html_editor_selection_get_font_color (EHTMLEditorSelection *selection,
|
|
GdkRGBA *rgba)
|
|
{
|
|
EHTMLEditorView *view;
|
|
gchar *color;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
if (!e_html_editor_view_get_html_mode (view)) {
|
|
g_object_unref (view);
|
|
*rgba = black;
|
|
return;
|
|
}
|
|
|
|
color = get_font_property (selection, "color");
|
|
if (!(color && *color)) {
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMHTMLElement *body;
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
body = webkit_dom_document_get_body (document);
|
|
|
|
g_free (color);
|
|
color = webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body));
|
|
if (!(color && *color)) {
|
|
*rgba = black;
|
|
g_object_unref (view);
|
|
g_free (color);
|
|
return;
|
|
}
|
|
}
|
|
|
|
gdk_rgba_parse (rgba, color);
|
|
g_free (color);
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_font_color:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @rgba: a #GdkRGBA
|
|
*
|
|
* Sets font color of current selection or letter at current cursor position to
|
|
* color defined in @rgba.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection,
|
|
const GdkRGBA *rgba)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
guint32 rgba_value;
|
|
gchar *color;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (!rgba)
|
|
rgba = &black;
|
|
|
|
rgba_value = e_rgba_to_value ((GdkRGBA *) rgba);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR;
|
|
color = g_strdup_printf ("#%06x", rgba_value);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_FONT_COLOR;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.string.from = g_strdup (selection->priv->font_color);
|
|
ev->data.string.to = g_strdup (color);
|
|
}
|
|
|
|
g_free (selection->priv->font_color);
|
|
selection->priv->font_color = g_strdup (color);
|
|
e_html_editor_view_exec_command (view, command, color);
|
|
g_free (color);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "font-color");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_get_font_name:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns name of font used in current selection or at letter at current cursor
|
|
* position.
|
|
*
|
|
* Returns: A string with font name. [transfer-none]
|
|
*/
|
|
const gchar *
|
|
e_html_editor_selection_get_font_name (EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMNode *node;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMCSSStyleDeclaration *css;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
g_object_unref (range);
|
|
|
|
g_free (selection->priv->font_family);
|
|
css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node));
|
|
selection->priv->font_family =
|
|
webkit_dom_css_style_declaration_get_property_value (css, "fontFamily");
|
|
|
|
g_object_unref (css);
|
|
|
|
return selection->priv->font_family;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_font_name:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @font_name: a font name to apply
|
|
*
|
|
* Sets font name of current selection or of letter at current cursor position
|
|
* to @font_name.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_font_name (EHTMLEditorSelection *selection,
|
|
const gchar *font_name)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME;
|
|
e_html_editor_view_exec_command (view, command, font_name);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "font-name");
|
|
}
|
|
|
|
/**
|
|
* e_editor_Selection_get_font_size:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns point size of current selection or of letter at current cursor position.
|
|
*/
|
|
guint
|
|
e_html_editor_selection_get_font_size (EHTMLEditorSelection *selection)
|
|
{
|
|
gchar *size;
|
|
guint size_int;
|
|
|
|
g_return_val_if_fail (
|
|
E_IS_HTML_EDITOR_SELECTION (selection),
|
|
E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL);
|
|
|
|
size = get_font_property (selection, "size");
|
|
if (!(size && *size)) {
|
|
g_free (size);
|
|
return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
|
|
}
|
|
|
|
size_int = atoi (size);
|
|
g_free (size);
|
|
|
|
if (size_int == 0)
|
|
return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
|
|
|
|
return size_int;
|
|
}
|
|
|
|
static WebKitDOMElement *
|
|
set_font_style (WebKitDOMDocument *document,
|
|
const gchar *element_name,
|
|
gboolean value)
|
|
{
|
|
WebKitDOMElement *element;
|
|
WebKitDOMNode *parent;
|
|
|
|
element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker");
|
|
parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
|
|
if (value) {
|
|
WebKitDOMNode *node;
|
|
WebKitDOMElement *el;
|
|
gchar *name;
|
|
|
|
el = webkit_dom_document_create_element (document, element_name, NULL);
|
|
webkit_dom_html_element_set_inner_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL);
|
|
|
|
node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (el), node, NULL);
|
|
name = webkit_dom_node_get_local_name (parent);
|
|
if (g_strcmp0 (name, element_name) == 0)
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (parent),
|
|
WEBKIT_DOM_NODE (el),
|
|
webkit_dom_node_get_next_sibling (parent),
|
|
NULL);
|
|
else
|
|
webkit_dom_node_insert_before (
|
|
parent,
|
|
WEBKIT_DOM_NODE (el),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
g_free (name);
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL);
|
|
|
|
return el;
|
|
} else {
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
|
|
|
|
/* Turning the formatting in the middle of element. */
|
|
if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))) {
|
|
WebKitDOMNode *clone;
|
|
WebKitDOMNode *sibling;
|
|
|
|
clone = webkit_dom_node_clone_node (
|
|
WEBKIT_DOM_NODE (parent), FALSE);
|
|
|
|
while ((sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))))
|
|
webkit_dom_node_insert_before (
|
|
clone,
|
|
sibling,
|
|
webkit_dom_node_get_first_child (clone),
|
|
NULL);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (parent),
|
|
clone,
|
|
webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
|
|
NULL);
|
|
}
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (parent),
|
|
WEBKIT_DOM_NODE (element),
|
|
webkit_dom_node_get_next_sibling (parent),
|
|
NULL);
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (parent),
|
|
node,
|
|
webkit_dom_node_get_next_sibling (parent),
|
|
NULL);
|
|
|
|
webkit_dom_html_element_insert_adjacent_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (parent),
|
|
"afterend",
|
|
UNICODE_ZERO_WIDTH_SPACE,
|
|
NULL);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_font_size:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @font_size: point size to apply
|
|
*
|
|
* Sets font size of current selection or of letter at current cursor position
|
|
* to @font_size.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection,
|
|
guint font_size)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
gchar *size_str;
|
|
guint current_font_size;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
current_font_size = e_html_editor_selection_get_font_size (selection);
|
|
if (current_font_size == font_size) {
|
|
g_object_unref (view);
|
|
return;
|
|
}
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_FONT_SIZE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = current_font_size;
|
|
ev->data.style.to = font_size;
|
|
}
|
|
|
|
selection->priv->font_size = font_size;
|
|
size_str = g_strdup_printf ("%d", font_size);
|
|
|
|
if (e_html_editor_selection_is_collapsed (selection)) {
|
|
WebKitDOMElement *font;
|
|
WebKitDOMDocument *document;
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
font = set_font_style (document, "font", font_size != 3);
|
|
if (font)
|
|
webkit_dom_element_set_attribute (font, "size", size_str, NULL);
|
|
e_html_editor_selection_restore (selection);
|
|
goto exit;
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE;
|
|
e_html_editor_view_exec_command (view, command, size_str);
|
|
|
|
/* Text in <font size="3"></font> (size 3 is our default size) is a little
|
|
* bit smaller than font outsize it. So move it outside of it. */
|
|
if (font_size == E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL) {
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *element;
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
element = webkit_dom_document_query_selector (document, "font[size=\"3\"]", NULL);
|
|
if (element) {
|
|
WebKitDOMNode *child;
|
|
|
|
while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))))
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
|
|
child,
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
|
|
remove_node (WEBKIT_DOM_NODE (element));
|
|
}
|
|
}
|
|
exit:
|
|
g_free (size_str);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "font-size");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_citation:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current paragraph is a citation.
|
|
*
|
|
* Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_citation (EHTMLEditorSelection *selection)
|
|
{
|
|
gboolean ret_val;
|
|
gchar *value, *text_content;
|
|
WebKitDOMNode *node;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return FALSE;
|
|
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
g_object_unref (range);
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (node))
|
|
return get_has_style (selection, "citation");
|
|
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
if (g_strcmp0 (text_content, "") == 0) {
|
|
g_free (text_content);
|
|
return FALSE;
|
|
}
|
|
g_free (text_content);
|
|
|
|
value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
|
|
|
|
/* citation == <blockquote type='cite'> */
|
|
if (g_strstr_len (value, -1, "cite"))
|
|
ret_val = TRUE;
|
|
else
|
|
ret_val = get_has_style (selection, "citation");
|
|
|
|
g_free (value);
|
|
return ret_val;
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
get_parent_indented_block (WebKitDOMNode *node)
|
|
{
|
|
WebKitDOMNode *parent, *block = NULL;
|
|
|
|
parent = webkit_dom_node_get_parent_node (node);
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented"))
|
|
block = parent;
|
|
|
|
while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) &&
|
|
!WEBKIT_DOM_IS_HTML_HTML_ELEMENT (parent)) {
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented"))
|
|
block = parent;
|
|
parent = webkit_dom_node_get_parent_node (parent);
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
static WebKitDOMElement*
|
|
get_element_for_inspection (WebKitDOMRange *range)
|
|
{
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_range_get_end_container (range, NULL);
|
|
/* No selection or whole body selected */
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
|
|
return NULL;
|
|
|
|
return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_indented:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current paragraph is indented. This does not include
|
|
* citations. To check, whether paragraph is a citation, use
|
|
* e_html_editor_selection_is_citation().
|
|
*
|
|
* Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_indented (EHTMLEditorSelection *selection)
|
|
{
|
|
WebKitDOMElement *element;
|
|
WebKitDOMRange *range;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return FALSE;
|
|
|
|
if (webkit_dom_range_get_collapsed (range, NULL)) {
|
|
element = get_element_for_inspection (range);
|
|
g_object_unref (range);
|
|
return element_has_class (element, "-x-evo-indented");
|
|
} else {
|
|
WebKitDOMNode *node;
|
|
gboolean ret_val;
|
|
|
|
node = webkit_dom_range_get_end_container (range, NULL);
|
|
/* No selection or whole body selected */
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
|
|
goto out;
|
|
|
|
element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
|
|
ret_val = element_has_class (element, "-x-evo-indented");
|
|
if (!ret_val)
|
|
goto out;
|
|
|
|
node = webkit_dom_range_get_start_container (range, NULL);
|
|
/* No selection or whole body selected */
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
|
|
goto out;
|
|
|
|
element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
|
|
ret_val = element_has_class (element, "-x-evo-indented");
|
|
|
|
g_object_unref (range);
|
|
return ret_val;
|
|
}
|
|
|
|
out:
|
|
g_object_unref (range);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
is_in_html_mode (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
gboolean ret_val;
|
|
|
|
g_return_val_if_fail (view != NULL, FALSE);
|
|
|
|
ret_val = e_html_editor_view_get_html_mode (view);
|
|
|
|
g_object_unref (view);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
static gint
|
|
get_list_level (WebKitDOMNode *node)
|
|
{
|
|
gint level = 0;
|
|
|
|
while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
|
|
if (node_is_list (node))
|
|
level++;
|
|
node = webkit_dom_node_get_parent_node (node);
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
static void
|
|
indent_list (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *item, *next_item;
|
|
gboolean after_selection_end = FALSE;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
item = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
|
|
gboolean html_mode = is_in_html_mode (selection);
|
|
WebKitDOMElement *list;
|
|
WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (item);
|
|
EHTMLEditorSelectionBlockFormat format;
|
|
|
|
format = e_html_editor_selection_get_list_format_from_node (source_list);
|
|
|
|
list = create_list_element (
|
|
selection, document, format, get_list_level (item), html_mode);
|
|
|
|
element_add_class (list, "-x-evo-indented");
|
|
|
|
webkit_dom_node_insert_before (
|
|
source_list, WEBKIT_DOM_NODE (list), item, NULL);
|
|
|
|
while (item && !after_selection_end) {
|
|
after_selection_end = webkit_dom_node_contains (
|
|
item, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
next_item = webkit_dom_node_get_next_sibling (item);
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (list), item, NULL);
|
|
|
|
item = next_item;
|
|
}
|
|
|
|
merge_lists_if_possible (WEBKIT_DOM_NODE (list));
|
|
}
|
|
}
|
|
|
|
static void
|
|
indent_block (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
WebKitDOMNode *block,
|
|
gint width)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
element = e_html_editor_selection_get_indented_element (
|
|
selection, document, width);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (block),
|
|
WEBKIT_DOM_NODE (element),
|
|
block,
|
|
NULL);
|
|
|
|
/* Remove style and let the paragraph inherit it from parent */
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph"))
|
|
webkit_dom_element_remove_attribute (
|
|
WEBKIT_DOM_ELEMENT (block), "style");
|
|
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element),
|
|
block,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_indent:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Indents current paragraph by one level.
|
|
*/
|
|
void
|
|
e_html_editor_selection_indent (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
gboolean after_selection_start = FALSE, after_selection_end = FALSE;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* If the selection was not saved, move it into the first child of body */
|
|
if (!selection_start_marker || !selection_end_marker) {
|
|
WebKitDOMHTMLElement *body;
|
|
WebKitDOMNode *child;
|
|
|
|
body = webkit_dom_document_get_body (document);
|
|
child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
|
|
|
|
add_selection_markers_into_element_start (
|
|
document,
|
|
WEBKIT_DOM_ELEMENT (child),
|
|
&selection_start_marker,
|
|
&selection_end_marker);
|
|
}
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_INDENT;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = 1;
|
|
ev->data.style.to = 1;
|
|
}
|
|
|
|
block = get_parent_indented_block (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!block)
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
while (block && !after_selection_end) {
|
|
gint ii, length, level, final_width = 0;
|
|
gint word_wrap_length = selection->priv->word_wrap_length;
|
|
WebKitDOMNode *next_block;
|
|
WebKitDOMNodeList *list;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
WEBKIT_DOM_ELEMENT (block),
|
|
".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
|
|
NULL);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
length = webkit_dom_node_list_get_length (list);
|
|
if (length == 0 && node_is_list_or_item (block)) {
|
|
indent_list (selection, document);
|
|
goto next;
|
|
}
|
|
|
|
if (length == 0) {
|
|
if (!after_selection_start) {
|
|
after_selection_start = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!after_selection_start)
|
|
goto next;
|
|
}
|
|
|
|
level = get_indentation_level (WEBKIT_DOM_ELEMENT (block));
|
|
|
|
final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
|
|
if (final_width < MINIMAL_PARAGRAPH_WIDTH &&
|
|
!is_in_html_mode (selection))
|
|
goto next;
|
|
|
|
indent_block (selection, document, block, final_width);
|
|
|
|
if (after_selection_end)
|
|
goto next;
|
|
}
|
|
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *block_to_process;
|
|
|
|
block_to_process = webkit_dom_node_list_item (list, ii);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block_to_process, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
if (!after_selection_start) {
|
|
after_selection_start = webkit_dom_node_contains (
|
|
block_to_process,
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!after_selection_start) {
|
|
g_object_unref (block_to_process);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
level = get_indentation_level (
|
|
WEBKIT_DOM_ELEMENT (block_to_process));
|
|
|
|
final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
|
|
if (final_width < MINIMAL_PARAGRAPH_WIDTH &&
|
|
!is_in_html_mode (selection)) {
|
|
g_object_unref (block_to_process);
|
|
continue;
|
|
}
|
|
|
|
indent_block (selection, document, block_to_process, final_width);
|
|
|
|
if (after_selection_end) {
|
|
g_object_unref (block_to_process);
|
|
break;
|
|
}
|
|
g_object_unref (block_to_process);
|
|
}
|
|
|
|
next:
|
|
g_object_unref (list);
|
|
|
|
if (!after_selection_end)
|
|
block = next_block;
|
|
}
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "indented");
|
|
}
|
|
|
|
static const gchar *
|
|
get_css_alignment_value_class (EHTMLEditorSelectionAlignment alignment)
|
|
{
|
|
if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
|
|
return ""; /* Left is by default on ltr */
|
|
|
|
if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER)
|
|
return "-x-evo-align-center";
|
|
|
|
if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT)
|
|
return "-x-evo-align-right";
|
|
|
|
return "";
|
|
}
|
|
|
|
static void
|
|
unindent_list (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
gboolean after = FALSE;
|
|
WebKitDOMElement *new_list;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
|
|
WebKitDOMNode *prev_item;
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
if (!selection_start_marker || !selection_end_marker)
|
|
return;
|
|
|
|
/* Copy elements from previous block to list */
|
|
item = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
source_list = webkit_dom_node_get_parent_node (item);
|
|
new_list = WEBKIT_DOM_ELEMENT (
|
|
webkit_dom_node_clone_node (source_list, FALSE));
|
|
current_list = source_list;
|
|
source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (source_list),
|
|
WEBKIT_DOM_NODE (source_list_clone),
|
|
webkit_dom_node_get_next_sibling (source_list),
|
|
NULL);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
|
|
element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
|
|
|
|
prev_item = source_list;
|
|
|
|
while (item) {
|
|
WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
|
|
|
|
if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
|
|
if (after)
|
|
prev_item = webkit_dom_node_append_child (
|
|
source_list_clone, WEBKIT_DOM_NODE (item), NULL);
|
|
else
|
|
prev_item = webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (prev_item),
|
|
item,
|
|
webkit_dom_node_get_next_sibling (prev_item),
|
|
NULL);
|
|
}
|
|
|
|
if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker)))
|
|
after = TRUE;
|
|
|
|
if (!next_item) {
|
|
if (after)
|
|
break;
|
|
|
|
current_list = webkit_dom_node_get_next_sibling (current_list);
|
|
next_item = webkit_dom_node_get_first_child (current_list);
|
|
}
|
|
item = next_item;
|
|
}
|
|
|
|
remove_node_if_empty (source_list_clone);
|
|
remove_node_if_empty (source_list);
|
|
}
|
|
|
|
static void
|
|
unindent_block (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
WebKitDOMNode *block)
|
|
{
|
|
gboolean before_node = TRUE;
|
|
gint word_wrap_length = selection->priv->word_wrap_length;
|
|
gint level, width;
|
|
EHTMLEditorSelectionAlignment alignment;
|
|
WebKitDOMElement *element;
|
|
WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL;
|
|
WebKitDOMNode *block_to_process, *node_clone = NULL, *child;
|
|
|
|
block_to_process = block;
|
|
|
|
alignment = e_html_editor_selection_get_alignment_from_node (block_to_process);
|
|
|
|
element = webkit_dom_node_get_parent_element (block_to_process);
|
|
|
|
if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element) &&
|
|
!element_has_class (element, "-x-evo-indented"))
|
|
return;
|
|
|
|
element_add_class (WEBKIT_DOM_ELEMENT (block_to_process), "-x-evo-to-unindent");
|
|
|
|
level = get_indentation_level (element);
|
|
width = word_wrap_length - SPACES_PER_INDENTATION * level;
|
|
|
|
/* Look if we have previous siblings, if so, we have to
|
|
* create new blockquote that will include them */
|
|
if (webkit_dom_node_get_previous_sibling (block_to_process))
|
|
prev_blockquote = e_html_editor_selection_get_indented_element (
|
|
selection, document, width);
|
|
|
|
/* Look if we have next siblings, if so, we have to
|
|
* create new blockquote that will include them */
|
|
if (webkit_dom_node_get_next_sibling (block_to_process))
|
|
next_blockquote = e_html_editor_selection_get_indented_element (
|
|
selection, document, width);
|
|
|
|
/* Copy nodes that are before / after the element that we want to unindent */
|
|
while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) {
|
|
if (webkit_dom_node_is_equal_node (child, block_to_process)) {
|
|
before_node = FALSE;
|
|
node_clone = webkit_dom_node_clone_node (child, TRUE);
|
|
remove_node (child);
|
|
continue;
|
|
}
|
|
|
|
webkit_dom_node_append_child (
|
|
before_node ?
|
|
WEBKIT_DOM_NODE (prev_blockquote) :
|
|
WEBKIT_DOM_NODE (next_blockquote),
|
|
child,
|
|
NULL);
|
|
}
|
|
|
|
if (node_clone) {
|
|
element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent");
|
|
|
|
/* Insert blockqoute with nodes that were before the element that we want to unindent */
|
|
if (prev_blockquote) {
|
|
if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
|
|
WEBKIT_DOM_NODE (prev_blockquote),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (level == 1 && element_has_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-paragraph")) {
|
|
e_html_editor_selection_set_paragraph_style (
|
|
selection, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, "");
|
|
element_add_class (
|
|
WEBKIT_DOM_ELEMENT (node_clone),
|
|
get_css_alignment_value_class (alignment));
|
|
}
|
|
|
|
/* Insert the unindented element */
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
|
|
node_clone,
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
} else {
|
|
g_warn_if_reached ();
|
|
}
|
|
|
|
/* Insert blockqoute with nodes that were after the element that we want to unindent */
|
|
if (next_blockquote) {
|
|
if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
|
|
WEBKIT_DOM_NODE (next_blockquote),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/* Remove old blockquote */
|
|
remove_node (WEBKIT_DOM_NODE (element));
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_unindent:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Unindents current paragraph by one level.
|
|
*/
|
|
void
|
|
e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
gboolean after_selection_start = FALSE, after_selection_end = FALSE;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* If the selection was not saved, move it into the first child of body */
|
|
if (!selection_start_marker || !selection_end_marker) {
|
|
WebKitDOMHTMLElement *body;
|
|
WebKitDOMNode *child;
|
|
|
|
body = webkit_dom_document_get_body (document);
|
|
child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
|
|
|
|
add_selection_markers_into_element_start (
|
|
document,
|
|
WEBKIT_DOM_ELEMENT (child),
|
|
&selection_start_marker,
|
|
&selection_end_marker);
|
|
}
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_INDENT;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
}
|
|
|
|
block = get_parent_indented_block (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!block)
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
while (block && !after_selection_end) {
|
|
gint ii, length;
|
|
WebKitDOMNode *next_block;
|
|
WebKitDOMNodeList *list;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
WEBKIT_DOM_ELEMENT (block),
|
|
".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
|
|
NULL);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
length = webkit_dom_node_list_get_length (list);
|
|
if (length == 0 && node_is_list_or_item (block)) {
|
|
unindent_list (selection, document);
|
|
goto next;
|
|
}
|
|
|
|
if (length == 0) {
|
|
if (!after_selection_start) {
|
|
after_selection_start = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!after_selection_start)
|
|
goto next;
|
|
}
|
|
|
|
unindent_block (selection, document, block);
|
|
|
|
if (after_selection_end)
|
|
goto next;
|
|
}
|
|
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *block_to_process;
|
|
|
|
block_to_process = webkit_dom_node_list_item (list, ii);
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block_to_process,
|
|
WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
if (!after_selection_start) {
|
|
after_selection_start = webkit_dom_node_contains (
|
|
block_to_process,
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
if (!after_selection_start) {
|
|
g_object_unref (block_to_process);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
unindent_block (selection, document, block_to_process);
|
|
|
|
if (after_selection_end) {
|
|
g_object_unref (block_to_process);
|
|
break;
|
|
}
|
|
|
|
g_object_unref (block_to_process);
|
|
}
|
|
next:
|
|
g_object_unref (list);
|
|
block = next_block;
|
|
}
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "indented");
|
|
}
|
|
|
|
typedef gboolean (*IsRightFormatNodeFunc) (WebKitDOMElement *element);
|
|
|
|
static gboolean
|
|
is_bold_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
if (element_has_tag (element, "b"))
|
|
return TRUE;
|
|
|
|
/* Headings are bold by default */
|
|
return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element);
|
|
}
|
|
|
|
static gboolean
|
|
html_editor_selection_is_font_format (EHTMLEditorSelection *selection,
|
|
IsRightFormatNodeFunc func,
|
|
gboolean *previous_value)
|
|
{
|
|
EHTMLEditorView *view;
|
|
gboolean ret_val = FALSE;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window = NULL;
|
|
WebKitDOMDOMSelection *dom_selection = NULL;
|
|
WebKitDOMNode *start, *end, *sibling;
|
|
WebKitDOMRange *range = NULL;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_val_if_fail (view != NULL, FALSE);
|
|
|
|
if (!e_html_editor_view_get_html_mode (view))
|
|
goto out;
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
if (!webkit_dom_dom_selection_get_range_count (dom_selection))
|
|
goto out;
|
|
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
if (!range)
|
|
goto out;
|
|
|
|
if (webkit_dom_range_get_collapsed (range, NULL) && previous_value) {
|
|
WebKitDOMNode *node;
|
|
gchar* text_content;
|
|
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
/* If we are changing the format of block we have to re-set the
|
|
* format property, otherwise it will be turned off because of no
|
|
* text in block. */
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
if (g_strcmp0 (text_content, "") == 0) {
|
|
g_free (text_content);
|
|
ret_val = *previous_value;
|
|
goto out;
|
|
}
|
|
g_free (text_content);
|
|
}
|
|
|
|
/* Range without start or end point is a wrong range. */
|
|
start = webkit_dom_range_get_start_container (range, NULL);
|
|
end = webkit_dom_range_get_end_container (range, NULL);
|
|
if (!start || !end)
|
|
goto out;
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (start))
|
|
start = webkit_dom_node_get_parent_node (start);
|
|
while (start && WEBKIT_DOM_IS_ELEMENT (start) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start)) {
|
|
/* Find the start point's parent node with given formatting. */
|
|
if (func (WEBKIT_DOM_ELEMENT (start))) {
|
|
ret_val = TRUE;
|
|
break;
|
|
}
|
|
start = webkit_dom_node_get_parent_node (start);
|
|
}
|
|
|
|
/* Start point doesn't have the given formatting. */
|
|
if (!ret_val)
|
|
goto out;
|
|
|
|
/* If the selection is collapsed, we can return early. */
|
|
if (webkit_dom_range_get_collapsed (range, NULL))
|
|
goto out;
|
|
|
|
/* The selection is in the same node and that node is supposed to have
|
|
* the same formatting (otherwise it is split up with formatting element. */
|
|
if (webkit_dom_node_is_same_node (
|
|
webkit_dom_range_get_start_container (range, NULL),
|
|
webkit_dom_range_get_end_container (range, NULL)))
|
|
goto out;
|
|
|
|
ret_val = FALSE;
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (end))
|
|
end = webkit_dom_node_get_parent_node (end);
|
|
while (end && WEBKIT_DOM_IS_ELEMENT (end) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end)) {
|
|
/* Find the end point's parent node with given formatting. */
|
|
if (func (WEBKIT_DOM_ELEMENT (end))) {
|
|
ret_val = TRUE;
|
|
break;
|
|
}
|
|
end = webkit_dom_node_get_parent_node (end);
|
|
}
|
|
|
|
if (!ret_val)
|
|
goto out;
|
|
|
|
ret_val = FALSE;
|
|
|
|
/* Now go between the end points and check the inner nodes for format validity. */
|
|
sibling = start;
|
|
while ((sibling = webkit_dom_node_get_next_sibling (sibling))) {
|
|
if (webkit_dom_node_is_same_node (sibling, end)) {
|
|
ret_val = TRUE;
|
|
goto out;
|
|
}
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (sibling))
|
|
goto out;
|
|
else if (func (WEBKIT_DOM_ELEMENT (sibling)))
|
|
continue;
|
|
else if (webkit_dom_node_get_first_child (sibling)) {
|
|
WebKitDOMNode *first_child;
|
|
|
|
first_child = webkit_dom_node_get_first_child (sibling);
|
|
if (!webkit_dom_node_get_next_sibling (first_child))
|
|
if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child)))
|
|
continue;
|
|
else
|
|
goto out;
|
|
else
|
|
goto out;
|
|
} else
|
|
goto out;
|
|
}
|
|
|
|
sibling = end;
|
|
while ((sibling = webkit_dom_node_get_previous_sibling (sibling))) {
|
|
if (webkit_dom_node_is_same_node (sibling, start))
|
|
break;
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (sibling))
|
|
goto out;
|
|
else if (func (WEBKIT_DOM_ELEMENT (sibling)))
|
|
continue;
|
|
else if (webkit_dom_node_get_first_child (sibling)) {
|
|
WebKitDOMNode *first_child;
|
|
|
|
first_child = webkit_dom_node_get_first_child (sibling);
|
|
if (!webkit_dom_node_get_next_sibling (first_child))
|
|
if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child)))
|
|
continue;
|
|
else
|
|
goto out;
|
|
else
|
|
goto out;
|
|
} else
|
|
goto out;
|
|
}
|
|
|
|
ret_val = TRUE;
|
|
out:
|
|
g_object_unref (view);
|
|
g_clear_object (&range);
|
|
g_clear_object (&dom_window);
|
|
g_clear_object (&dom_selection);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_bold:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is bold.
|
|
*
|
|
* Returns @TRUE when selection is bold, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_bold (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
selection->priv->is_bold = html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_bold_element, &selection->priv->is_bold);
|
|
|
|
return selection->priv->is_bold;
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_set_font_style (EHTMLEditorSelection *selection,
|
|
EHTMLEditorViewCommand command,
|
|
gboolean value)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
|
|
ev->type = HISTORY_BOLD;
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
|
|
ev->type = HISTORY_ITALIC;
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
|
|
ev->type = HISTORY_UNDERLINE;
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
|
|
ev->type = HISTORY_STRIKETHROUGH;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = !value;
|
|
ev->data.style.to = value;
|
|
}
|
|
|
|
if (e_html_editor_selection_is_collapsed (selection)) {
|
|
WebKitDOMDocument *document;
|
|
const gchar *element_name = NULL;
|
|
|
|
if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
|
|
element_name = "b";
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
|
|
element_name = "i";
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
|
|
element_name = "u";
|
|
else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
|
|
element_name = "strike";
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
if (element_name)
|
|
set_font_style (document, element_name, value);
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
goto exit;
|
|
}
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
e_html_editor_view_exec_command (view, command, NULL);
|
|
exit:
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_bold:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @bold: @TRUE to enable bold, @FALSE to disable
|
|
*
|
|
* Toggles bold formatting of current selection or letter at current cursor
|
|
* position, depending on whether @bold is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_bold (EHTMLEditorSelection *selection,
|
|
gboolean bold)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_bold (selection) == bold)
|
|
return;
|
|
|
|
selection->priv->is_bold = bold;
|
|
|
|
html_editor_selection_set_font_style (
|
|
selection, E_HTML_EDITOR_VIEW_COMMAND_BOLD, bold);
|
|
|
|
g_object_notify (G_OBJECT (selection), "bold");
|
|
}
|
|
|
|
static gboolean
|
|
is_italic_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
return element_has_tag (element, "i") || element_has_tag (element, "address");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_italic:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is italic.
|
|
*
|
|
* Returns @TRUE when selection is italic, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_italic (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
selection->priv->is_italic = html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_italic_element, &selection->priv->is_italic);
|
|
|
|
return selection->priv->is_italic;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_italic:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @italic: @TRUE to enable italic, @FALSE to disable
|
|
*
|
|
* Toggles italic formatting of current selection or letter at current cursor
|
|
* position, depending on whether @italic is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_italic (EHTMLEditorSelection *selection,
|
|
gboolean italic)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_italic (selection) == italic)
|
|
return;
|
|
|
|
selection->priv->is_italic = italic;
|
|
|
|
html_editor_selection_set_font_style (
|
|
selection, E_HTML_EDITOR_VIEW_COMMAND_ITALIC, italic);
|
|
|
|
g_object_notify (G_OBJECT (selection), "italic");
|
|
}
|
|
|
|
static gboolean
|
|
is_monospaced_element (WebKitDOMElement *element)
|
|
{
|
|
gchar *value;
|
|
gboolean ret_val = FALSE;
|
|
|
|
if (!element)
|
|
return FALSE;
|
|
|
|
if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
value = webkit_dom_element_get_attribute (element, "face");
|
|
|
|
if (g_strcmp0 (value, "monospace") == 0)
|
|
ret_val = TRUE;
|
|
|
|
g_free (value);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_monospaced:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is monospaced.
|
|
*
|
|
* Returns @TRUE when selection is monospaced, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_monospaced (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
selection->priv->is_monospaced = html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_monospaced_element, &selection->priv->is_monospaced);
|
|
|
|
return selection->priv->is_monospaced;
|
|
}
|
|
|
|
static void
|
|
monospace_selection (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
WebKitDOMElement *monospaced_element)
|
|
{
|
|
gboolean selection_end = FALSE;
|
|
gboolean first = TRUE;
|
|
gint length, ii;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *sibling, *node, *monospace, *block;
|
|
WebKitDOMNodeList *list;
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
selection_end_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
|
|
block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)));
|
|
|
|
monospace = WEBKIT_DOM_NODE (monospaced_element);
|
|
node = WEBKIT_DOM_NODE (selection_start_marker);
|
|
/* Go through first block in selection. */
|
|
while (block && node && !webkit_dom_node_is_same_node (block, node)) {
|
|
if (webkit_dom_node_get_next_sibling (node)) {
|
|
/* Prepare the monospaced element. */
|
|
monospace = webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
first ? monospace : webkit_dom_node_clone_node (monospace, FALSE),
|
|
first ? node : webkit_dom_node_get_next_sibling (node),
|
|
NULL);
|
|
} else
|
|
break;
|
|
|
|
/* Move the nodes into monospaced element. */
|
|
while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) {
|
|
webkit_dom_node_append_child (monospace, sibling, NULL);
|
|
if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker), sibling)) {
|
|
selection_end = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
node = webkit_dom_node_get_parent_node (monospace);
|
|
first = FALSE;
|
|
}
|
|
|
|
/* Just one block was selected. */
|
|
if (selection_end)
|
|
goto out;
|
|
|
|
/* Middle blocks (blocks not containing the end of the selection. */
|
|
block = webkit_dom_node_get_next_sibling (block);
|
|
while (block && !selection_end) {
|
|
WebKitDOMNode *next_block;
|
|
|
|
selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
if (selection_end)
|
|
break;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
monospace = webkit_dom_node_insert_before (
|
|
block,
|
|
webkit_dom_node_clone_node (monospace, FALSE),
|
|
webkit_dom_node_get_first_child (block),
|
|
NULL);
|
|
|
|
while (((sibling = webkit_dom_node_get_next_sibling (monospace))))
|
|
webkit_dom_node_append_child (monospace, sibling, NULL);
|
|
|
|
block = next_block;
|
|
}
|
|
|
|
/* Block containing the end of selection. */
|
|
node = WEBKIT_DOM_NODE (selection_end_marker);
|
|
while (block && node && !webkit_dom_node_is_same_node (block, node)) {
|
|
monospace = webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
webkit_dom_node_clone_node (monospace, FALSE),
|
|
webkit_dom_node_get_next_sibling (node),
|
|
NULL);
|
|
|
|
while (((sibling = webkit_dom_node_get_previous_sibling (monospace)))) {
|
|
webkit_dom_node_insert_before (
|
|
monospace,
|
|
sibling,
|
|
webkit_dom_node_get_first_child (monospace),
|
|
NULL);
|
|
}
|
|
|
|
node = webkit_dom_node_get_parent_node (monospace);
|
|
}
|
|
out:
|
|
/* Merge all the monospace elements inside other monospace elements. */
|
|
list = webkit_dom_document_query_selector_all (
|
|
document, "font[face=monospace] > font[face=monospace]", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *item;
|
|
WebKitDOMNode *child;
|
|
|
|
item = webkit_dom_node_list_item (list, ii);
|
|
while ((child = webkit_dom_node_get_first_child (item))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (item),
|
|
child,
|
|
item,
|
|
NULL);
|
|
}
|
|
remove_node (item);
|
|
g_object_unref (item);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
/* Merge all the adjacent monospace elements. */
|
|
list = webkit_dom_document_query_selector_all (
|
|
document, "font[face=monospace] + font[face=monospace]", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *item;
|
|
WebKitDOMNode *child;
|
|
|
|
item = webkit_dom_node_list_item (list, ii);
|
|
/* The + CSS selector will return some false positives as it doesn't
|
|
* take text between elements into account so it will return this:
|
|
* <font face="monospace">xx</font>yy<font face="monospace">zz</font>
|
|
* as valid, but it isn't so we have to check if previous node
|
|
* is indeed element or not. */
|
|
if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item))) {
|
|
while ((child = webkit_dom_node_get_first_child (item))) {
|
|
webkit_dom_node_append_child (
|
|
webkit_dom_node_get_previous_sibling (item), child, NULL);
|
|
}
|
|
remove_node (item);
|
|
}
|
|
g_object_unref (item);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
}
|
|
|
|
static void
|
|
unmonospace_selection (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
WebKitDOMElement *selection_start_marker;
|
|
WebKitDOMElement *selection_end_marker;
|
|
WebKitDOMElement *selection_start_clone;
|
|
WebKitDOMElement *selection_end_clone;
|
|
WebKitDOMNode *sibling, *node;
|
|
gboolean selection_end = FALSE;
|
|
WebKitDOMNode *block, *clone, *monospace;
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
selection_end_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
|
|
block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)));
|
|
|
|
node = WEBKIT_DOM_NODE (selection_start_marker);
|
|
monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
|
|
while (monospace && !is_monospaced_element (WEBKIT_DOM_ELEMENT (monospace)))
|
|
monospace = webkit_dom_node_get_parent_node (monospace);
|
|
|
|
/* No monospaced element was found as a parent of selection start node. */
|
|
if (!monospace)
|
|
goto out;
|
|
|
|
/* Make a clone of current monospaced element. */
|
|
clone = webkit_dom_node_clone_node (monospace, TRUE);
|
|
|
|
/* First block */
|
|
/* Remove all the nodes that are after the selection start point as they
|
|
* will be in the cloned node. */
|
|
while (monospace && node && !webkit_dom_node_is_same_node (monospace, node)) {
|
|
WebKitDOMNode *tmp;
|
|
while (((sibling = webkit_dom_node_get_next_sibling (node))))
|
|
remove_node (sibling);
|
|
|
|
tmp = webkit_dom_node_get_parent_node (node);
|
|
if (webkit_dom_node_get_next_sibling (node))
|
|
remove_node (node);
|
|
node = tmp;
|
|
}
|
|
|
|
selection_start_clone = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-start-marker", NULL);
|
|
selection_end_clone = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* No selection start node in the block where it is supposed to be, return. */
|
|
if (!selection_start_clone)
|
|
goto out;
|
|
|
|
/* Remove all the nodes until we hit the selection start point as these
|
|
* nodes will stay monospaced and they are already in original element. */
|
|
node = webkit_dom_node_get_first_child (clone);
|
|
while (node) {
|
|
WebKitDOMNode *next_sibling;
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (node);
|
|
if (webkit_dom_node_get_first_child (node)) {
|
|
if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_start_clone))) {
|
|
node = webkit_dom_node_get_first_child (node);
|
|
continue;
|
|
} else
|
|
remove_node (node);
|
|
} else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_start_clone)))
|
|
break;
|
|
else
|
|
remove_node (node);
|
|
|
|
node = next_sibling;
|
|
}
|
|
|
|
/* Insert the clone into the tree. Do it after the previous clean up. If
|
|
* we would do it the other way the line would contain duplicated text nodes
|
|
* and the block would be expading and shrinking while we would modify it. */
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (monospace),
|
|
clone,
|
|
webkit_dom_node_get_next_sibling (monospace),
|
|
NULL);
|
|
|
|
/* Move selection start point the right place. */
|
|
remove_node (WEBKIT_DOM_NODE (selection_start_marker));
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
WEBKIT_DOM_NODE (selection_start_clone),
|
|
clone,
|
|
NULL);
|
|
|
|
/* Move all the nodes the are supposed to lose the monospace formatting
|
|
* out of monospaced element. */
|
|
node = webkit_dom_node_get_first_child (clone);
|
|
while (node) {
|
|
WebKitDOMNode *next_sibling;
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (node);
|
|
if (webkit_dom_node_get_first_child (node)) {
|
|
if (selection_end_clone &&
|
|
webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_clone))) {
|
|
node = webkit_dom_node_get_first_child (node);
|
|
continue;
|
|
} else
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
} else if (selection_end_clone &&
|
|
webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_clone))) {
|
|
selection_end = TRUE;
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
break;
|
|
} else
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
|
|
node = next_sibling;
|
|
}
|
|
|
|
if (!webkit_dom_node_get_first_child (clone))
|
|
remove_node (clone);
|
|
|
|
/* Just one block was selected and we hit the selection end point. */
|
|
if (selection_end)
|
|
goto out;
|
|
|
|
/* Middle blocks */
|
|
block = webkit_dom_node_get_next_sibling (block);
|
|
while (block && !selection_end) {
|
|
WebKitDOMNode *next_block, *child, *parent;
|
|
WebKitDOMElement *monospaced_element;
|
|
|
|
selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
if (selection_end)
|
|
break;
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
/* Find the monospaced element and move all the nodes from it and
|
|
* finally remove it. */
|
|
monospaced_element = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block), "font[face=monospace]", NULL);
|
|
if (!monospaced_element)
|
|
break;
|
|
|
|
parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospaced_element));
|
|
while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospaced_element)))) {
|
|
webkit_dom_node_insert_before (
|
|
parent, child, WEBKIT_DOM_NODE (monospaced_element), NULL);
|
|
}
|
|
|
|
remove_node (WEBKIT_DOM_NODE (monospaced_element));
|
|
|
|
block = next_block;
|
|
}
|
|
|
|
/* End block */
|
|
node = WEBKIT_DOM_NODE (selection_end_marker);
|
|
monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker));
|
|
while (monospace && !is_monospaced_element (WEBKIT_DOM_ELEMENT (monospace)))
|
|
monospace = webkit_dom_node_get_parent_node (monospace);
|
|
|
|
/* No monospaced element was found as a parent of selection end node. */
|
|
if (!monospace)
|
|
return;
|
|
|
|
clone = WEBKIT_DOM_NODE (monospace);
|
|
node = webkit_dom_node_get_first_child (clone);
|
|
/* Move all the nodes that are supposed to lose the monospaced formatting
|
|
* out of the monospaced element. */
|
|
while (node) {
|
|
WebKitDOMNode *next_sibling;
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (node);
|
|
if (webkit_dom_node_get_first_child (node)) {
|
|
if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_marker))) {
|
|
node = webkit_dom_node_get_first_child (node);
|
|
continue;
|
|
} else
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
} else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_marker))) {
|
|
selection_end = TRUE;
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
break;
|
|
} else {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (clone),
|
|
node,
|
|
clone,
|
|
NULL);
|
|
}
|
|
|
|
node = next_sibling;
|
|
}
|
|
|
|
if (!webkit_dom_node_get_first_child (clone))
|
|
remove_node (clone);
|
|
out:
|
|
e_html_editor_selection_restore (selection);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_monospaced:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @monospaced: @TRUE to enable monospaced, @FALSE to disable
|
|
*
|
|
* Toggles monospaced formatting of current selection or letter at current cursor
|
|
* position, depending on whether @monospaced is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
|
|
gboolean monospaced)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_monospaced (selection) == monospaced)
|
|
return;
|
|
|
|
selection->priv->is_monospaced = monospaced;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
if (!range)
|
|
return;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_MONOSPACE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = !monospaced;
|
|
ev->data.style.to = monospaced;
|
|
}
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
if (monospaced) {
|
|
guint font_size;
|
|
WebKitDOMElement *monospace;
|
|
|
|
monospace = webkit_dom_document_create_element (
|
|
document, "font", NULL);
|
|
webkit_dom_element_set_attribute (
|
|
monospace, "face", "monospace", NULL);
|
|
|
|
font_size = selection->priv->font_size;
|
|
if (font_size != 0) {
|
|
gchar *font_size_str;
|
|
|
|
font_size_str = g_strdup_printf ("%d", font_size);
|
|
webkit_dom_element_set_attribute (
|
|
monospace, "size", font_size_str, NULL);
|
|
g_free (font_size_str);
|
|
}
|
|
|
|
if (!webkit_dom_range_get_collapsed (range, NULL))
|
|
monospace_selection (selection, document, monospace);
|
|
else {
|
|
/* https://bugs.webkit.org/show_bug.cgi?id=15256 */
|
|
webkit_dom_html_element_set_inner_html (
|
|
WEBKIT_DOM_HTML_ELEMENT (monospace),
|
|
UNICODE_ZERO_WIDTH_SPACE,
|
|
NULL);
|
|
webkit_dom_range_insert_node (
|
|
range, WEBKIT_DOM_NODE (monospace), NULL);
|
|
|
|
e_html_editor_selection_move_caret_into_element (
|
|
document, monospace, FALSE);
|
|
}
|
|
} else {
|
|
gboolean is_bold, is_italic, is_underline, is_strikethrough;
|
|
guint font_size;
|
|
WebKitDOMElement *tt_element;
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_range_get_end_container (range, NULL);
|
|
if (WEBKIT_DOM_IS_ELEMENT (node) &&
|
|
is_monospaced_element (WEBKIT_DOM_ELEMENT (node))) {
|
|
tt_element = WEBKIT_DOM_ELEMENT (node);
|
|
} else {
|
|
tt_element = e_html_editor_dom_node_find_parent_element (node, "FONT");
|
|
|
|
if (!is_monospaced_element (tt_element)) {
|
|
g_object_unref (view);
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
g_free (ev);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Save current formatting */
|
|
is_bold = selection->priv->is_bold;
|
|
is_italic = selection->priv->is_italic;
|
|
is_underline = selection->priv->is_underline;
|
|
is_strikethrough = selection->priv->is_strikethrough;
|
|
font_size = selection->priv->font_size;
|
|
|
|
if (!webkit_dom_range_get_collapsed (range, NULL))
|
|
unmonospace_selection (selection, document);
|
|
else {
|
|
e_html_editor_selection_save (selection);
|
|
set_font_style (document, "", FALSE);
|
|
e_html_editor_selection_restore (selection);
|
|
}
|
|
|
|
/* Re-set formatting */
|
|
if (is_bold)
|
|
e_html_editor_selection_set_bold (selection, TRUE);
|
|
if (is_italic)
|
|
e_html_editor_selection_set_italic (selection, TRUE);
|
|
if (is_underline)
|
|
e_html_editor_selection_set_underline (selection, TRUE);
|
|
if (is_strikethrough)
|
|
e_html_editor_selection_set_strikethrough (selection, TRUE);
|
|
|
|
if (font_size)
|
|
e_html_editor_selection_set_font_size (selection, font_size);
|
|
}
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "monospaced");
|
|
}
|
|
|
|
static gboolean
|
|
is_strikethrough_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
return element_has_tag (element, "strike");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_strikethrough:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is striked through.
|
|
*
|
|
* Returns @TRUE when selection is striked through, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_strikethrough (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
selection->priv->is_strikethrough = html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_strikethrough_element, &selection->priv->is_strikethrough);
|
|
|
|
return selection->priv->is_strikethrough;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_strikethrough:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
|
|
*
|
|
* Toggles strike through formatting of current selection or letter at current
|
|
* cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_strikethrough (EHTMLEditorSelection *selection,
|
|
gboolean strikethrough)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_strikethrough (selection) == strikethrough)
|
|
return;
|
|
|
|
selection->priv->is_strikethrough = strikethrough;
|
|
|
|
html_editor_selection_set_font_style (
|
|
selection, E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, strikethrough);
|
|
|
|
g_object_notify (G_OBJECT (selection), "strikethrough");
|
|
}
|
|
|
|
static gboolean
|
|
is_subscript_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
return element_has_tag (element, "sub");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_subscript:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is in subscript.
|
|
*
|
|
* Returns @TRUE when selection is in subscript, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_subscript (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
return html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_subscript_element, NULL);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_subscript:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @subscript: @TRUE to enable subscript, @FALSE to disable
|
|
*
|
|
* Toggles subscript of current selection or letter at current cursor position,
|
|
* depending on whether @subscript is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_subscript (EHTMLEditorSelection *selection,
|
|
gboolean subscript)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_subscript (selection) == subscript)
|
|
return;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT;
|
|
e_html_editor_view_exec_command (view, command, NULL);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "subscript");
|
|
}
|
|
|
|
static gboolean
|
|
is_superscript_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
return element_has_tag (element, "sup");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_superscript:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is in superscript.
|
|
*
|
|
* Returns @TRUE when selection is in superscript, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_superscript (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
return html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_superscript_element, NULL);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_superscript:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @superscript: @TRUE to enable superscript, @FALSE to disable
|
|
*
|
|
* Toggles superscript of current selection or letter at current cursor position,
|
|
* depending on whether @superscript is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_superscript (EHTMLEditorSelection *selection,
|
|
gboolean superscript)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_superscript (selection) == superscript)
|
|
return;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT;
|
|
e_html_editor_view_exec_command (view, command, NULL);
|
|
|
|
g_object_unref (view);
|
|
|
|
g_object_notify (G_OBJECT (selection), "superscript");
|
|
}
|
|
|
|
static gboolean
|
|
is_underline_element (WebKitDOMElement *element)
|
|
{
|
|
if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
|
|
return FALSE;
|
|
|
|
return element_has_tag (element, "u");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_is_underline:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Returns whether current selection or letter at current cursor position
|
|
* is underlined.
|
|
*
|
|
* Returns @TRUE when selection is underlined, @FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
e_html_editor_selection_is_underline (EHTMLEditorSelection *selection)
|
|
{
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
|
|
|
|
selection->priv->is_underline = html_editor_selection_is_font_format (
|
|
selection, (IsRightFormatNodeFunc) is_underline_element, &selection->priv->is_underline);
|
|
|
|
return selection->priv->is_underline;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_set_underline:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @underline: @TRUE to enable underline, @FALSE to disable
|
|
*
|
|
* Toggles underline formatting of current selection or letter at current
|
|
* cursor position, depending on whether @underline is @TRUE or @FALSE.
|
|
*/
|
|
void
|
|
e_html_editor_selection_set_underline (EHTMLEditorSelection *selection,
|
|
gboolean underline)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
if (e_html_editor_selection_is_underline (selection) == underline)
|
|
return;
|
|
|
|
selection->priv->is_underline = underline;
|
|
|
|
html_editor_selection_set_font_style (
|
|
selection, E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, underline);
|
|
|
|
g_object_notify (G_OBJECT (selection), "underline");
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_unlink:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Removes any links (<A> elements) from current selection or at current
|
|
* cursor position.
|
|
*/
|
|
void
|
|
e_html_editor_selection_unlink (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
gchar *text;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMElement *link;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
link = e_html_editor_dom_node_find_parent_element (
|
|
webkit_dom_range_get_start_container (range, NULL), "A");
|
|
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
|
|
if (!link) {
|
|
WebKitDOMNode *node;
|
|
|
|
/* get element that was clicked on */
|
|
node = webkit_dom_range_get_common_ancestor_container (range, NULL);
|
|
if (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
|
|
link = e_html_editor_dom_node_find_parent_element (node, "A");
|
|
if (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link)) {
|
|
g_object_unref (range);
|
|
return;
|
|
}
|
|
} else
|
|
link = WEBKIT_DOM_ELEMENT (node);
|
|
}
|
|
|
|
g_object_unref (range);
|
|
|
|
if (!link)
|
|
return;
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
EHTMLEditorViewHistoryEvent *ev;
|
|
WebKitDOMDocumentFragment *fragment;
|
|
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_REMOVE_LINK;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
|
|
fragment = webkit_dom_document_create_document_fragment (document);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (fragment),
|
|
webkit_dom_node_clone_node (WEBKIT_DOM_NODE (link), TRUE),
|
|
NULL);
|
|
ev->data.fragment = fragment;
|
|
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
text = webkit_dom_html_element_get_inner_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (link));
|
|
webkit_dom_html_element_set_outer_html (
|
|
WEBKIT_DOM_HTML_ELEMENT (link), text, NULL);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_create_link:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @uri: destination of the new link
|
|
*
|
|
* Converts current selection into a link pointing to @url.
|
|
*/
|
|
void
|
|
e_html_editor_selection_create_link (EHTMLEditorSelection *selection,
|
|
const gchar *uri)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (uri != NULL && *uri != '\0');
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK;
|
|
e_html_editor_view_exec_command (view, command, uri);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_insert_text:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @plain_text: text to insert
|
|
*
|
|
* Inserts @plain_text at current cursor position. When a text range is selected,
|
|
* it will be replaced by @plain_text.
|
|
*/
|
|
void
|
|
e_html_editor_selection_insert_text (EHTMLEditorSelection *selection,
|
|
const gchar *plain_text)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (plain_text != NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_PASTE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.string.from = NULL;
|
|
ev->data.string.to = g_strdup (plain_text);
|
|
}
|
|
|
|
e_html_editor_view_convert_and_insert_plain_text (view, plain_text);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_insert_html:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @html_text: an HTML code to insert
|
|
*
|
|
* Insert @html_text into document at current cursor position. When a text range
|
|
* is selected, it will be replaced by @html_text.
|
|
*/
|
|
void
|
|
e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
|
|
const gchar *html_text)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewCommand command;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (html_text != NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_INSERT_HTML;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.string.from = NULL;
|
|
ev->data.string.to = g_strdup (html_text);
|
|
}
|
|
|
|
command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML;
|
|
if (e_html_editor_view_get_html_mode (view)) {
|
|
e_html_editor_view_exec_command (view, command, html_text);
|
|
if (strstr (html_text, "id=\"-x-evo-selection-start-marker\""))
|
|
e_html_editor_selection_restore (selection);
|
|
e_html_editor_view_check_magic_links (view, FALSE);
|
|
e_html_editor_view_force_spell_check_in_viewport (view);
|
|
|
|
e_html_editor_selection_scroll_to_caret (selection);
|
|
} else
|
|
e_html_editor_view_convert_and_insert_html_to_plain_text (
|
|
view, html_text);
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_insert_as_text (EHTMLEditorSelection *selection,
|
|
const gchar *html_text)
|
|
{
|
|
EHTMLEditorView *view;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (html_text != NULL);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
e_html_editor_view_convert_and_insert_html_to_plain_text (view, html_text);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/************************* image_load_and_insert_async() *************************/
|
|
|
|
typedef struct _LoadContext LoadContext;
|
|
|
|
struct _LoadContext {
|
|
EHTMLEditorSelection *selection;
|
|
WebKitDOMElement *element;
|
|
GInputStream *input_stream;
|
|
GOutputStream *output_stream;
|
|
GFile *file;
|
|
GFileInfo *file_info;
|
|
goffset total_num_bytes;
|
|
gssize bytes_read;
|
|
const gchar *content_type;
|
|
const gchar *filename;
|
|
gchar buffer[4096];
|
|
};
|
|
|
|
/* Forward Declaration */
|
|
static void
|
|
image_load_stream_read_cb (GInputStream *input_stream,
|
|
GAsyncResult *result,
|
|
LoadContext *load_context);
|
|
|
|
static LoadContext *
|
|
image_load_context_new (EHTMLEditorSelection *selection)
|
|
{
|
|
LoadContext *load_context;
|
|
|
|
load_context = g_slice_new0 (LoadContext);
|
|
load_context->selection = selection;
|
|
|
|
return load_context;
|
|
}
|
|
|
|
static void
|
|
image_load_context_free (LoadContext *load_context)
|
|
{
|
|
if (load_context->input_stream != NULL)
|
|
g_object_unref (load_context->input_stream);
|
|
|
|
if (load_context->output_stream != NULL)
|
|
g_object_unref (load_context->output_stream);
|
|
|
|
if (load_context->file_info != NULL)
|
|
g_object_unref (load_context->file_info);
|
|
|
|
if (load_context->file != NULL)
|
|
g_object_unref (load_context->file);
|
|
|
|
g_slice_free (LoadContext, load_context);
|
|
}
|
|
|
|
static void
|
|
replace_base64_image_src (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *element,
|
|
const gchar *base64_content,
|
|
const gchar *filename,
|
|
const gchar *uri)
|
|
{
|
|
EHTMLEditorView *view;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
e_html_editor_view_set_changed (view, TRUE);
|
|
g_object_unref (view);
|
|
|
|
if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element))
|
|
webkit_dom_html_image_element_set_src (
|
|
WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
|
|
base64_content);
|
|
else
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element),
|
|
"background",
|
|
base64_content,
|
|
NULL);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-name",
|
|
filename ? filename : "", NULL);
|
|
}
|
|
|
|
static void
|
|
insert_base64_image (EHTMLEditorSelection *selection,
|
|
const gchar *base64_content,
|
|
const gchar *filename,
|
|
const gchar *uri)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper;
|
|
WebKitDOMText *text;
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
e_html_editor_view_set_changed (view, TRUE);
|
|
|
|
if (!e_html_editor_selection_is_collapsed (selection))
|
|
e_html_editor_view_exec_command (
|
|
view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
|
|
|
|
e_html_editor_selection_save (selection);
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_IMAGE;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
}
|
|
|
|
resizable_wrapper =
|
|
webkit_dom_document_create_element (document, "span", NULL);
|
|
webkit_dom_element_set_attribute (
|
|
resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL);
|
|
|
|
element = webkit_dom_document_create_element (document, "img", NULL);
|
|
webkit_dom_html_image_element_set_src (
|
|
WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
|
|
base64_content);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
|
|
webkit_dom_element_set_attribute (
|
|
WEBKIT_DOM_ELEMENT (element), "data-name",
|
|
filename ? filename : "", NULL);
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (resizable_wrapper),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (
|
|
WEBKIT_DOM_NODE (selection_start_marker)),
|
|
WEBKIT_DOM_NODE (resizable_wrapper),
|
|
WEBKIT_DOM_NODE (selection_start_marker),
|
|
NULL);
|
|
|
|
/* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
|
|
* caret on right position */
|
|
text = webkit_dom_document_create_text_node (
|
|
document, UNICODE_ZERO_WIDTH_SPACE);
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (
|
|
WEBKIT_DOM_NODE (selection_start_marker)),
|
|
WEBKIT_DOM_NODE (text),
|
|
WEBKIT_DOM_NODE (selection_start_marker),
|
|
NULL);
|
|
|
|
if (ev) {
|
|
WebKitDOMDocumentFragment *fragment;
|
|
WebKitDOMNode *node;
|
|
|
|
fragment = webkit_dom_document_create_document_fragment (document);
|
|
node = webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (fragment),
|
|
webkit_dom_node_clone_node (WEBKIT_DOM_NODE (resizable_wrapper), TRUE),
|
|
NULL);
|
|
webkit_dom_html_element_insert_adjacent_html (
|
|
WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "​", NULL);
|
|
ev->data.fragment = fragment;
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
static void
|
|
image_load_finish (LoadContext *load_context)
|
|
{
|
|
EHTMLEditorSelection *selection;
|
|
GMemoryOutputStream *output_stream;
|
|
gchar *base64_encoded, *mime_type, *output, *uri;
|
|
gsize size;
|
|
gpointer data;
|
|
|
|
output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
|
|
|
|
selection = load_context->selection;
|
|
|
|
mime_type = g_content_type_get_mime_type (load_context->content_type);
|
|
|
|
data = g_memory_output_stream_get_data (output_stream);
|
|
size = g_memory_output_stream_get_data_size (output_stream);
|
|
uri = g_file_get_uri (load_context->file);
|
|
|
|
base64_encoded = g_base64_encode ((const guchar *) data, size);
|
|
output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
|
|
if (load_context->element)
|
|
replace_base64_image_src (
|
|
selection, load_context->element, output, load_context->filename, uri);
|
|
else
|
|
insert_base64_image (selection, output, load_context->filename, uri);
|
|
|
|
g_free (base64_encoded);
|
|
g_free (output);
|
|
g_free (mime_type);
|
|
g_free (uri);
|
|
|
|
image_load_context_free (load_context);
|
|
}
|
|
|
|
static void
|
|
image_load_write_cb (GOutputStream *output_stream,
|
|
GAsyncResult *result,
|
|
LoadContext *load_context)
|
|
{
|
|
GInputStream *input_stream;
|
|
gssize bytes_written;
|
|
GError *error = NULL;
|
|
|
|
bytes_written = g_output_stream_write_finish (
|
|
output_stream, result, &error);
|
|
|
|
if (error) {
|
|
image_load_context_free (load_context);
|
|
return;
|
|
}
|
|
|
|
input_stream = load_context->input_stream;
|
|
|
|
if (bytes_written < load_context->bytes_read) {
|
|
g_memmove (
|
|
load_context->buffer,
|
|
load_context->buffer + bytes_written,
|
|
load_context->bytes_read - bytes_written);
|
|
load_context->bytes_read -= bytes_written;
|
|
|
|
g_output_stream_write_async (
|
|
output_stream,
|
|
load_context->buffer,
|
|
load_context->bytes_read,
|
|
G_PRIORITY_DEFAULT, NULL,
|
|
(GAsyncReadyCallback) image_load_write_cb,
|
|
load_context);
|
|
} else
|
|
g_input_stream_read_async (
|
|
input_stream,
|
|
load_context->buffer,
|
|
sizeof (load_context->buffer),
|
|
G_PRIORITY_DEFAULT, NULL,
|
|
(GAsyncReadyCallback) image_load_stream_read_cb,
|
|
load_context);
|
|
}
|
|
|
|
static void
|
|
image_load_stream_read_cb (GInputStream *input_stream,
|
|
GAsyncResult *result,
|
|
LoadContext *load_context)
|
|
{
|
|
GOutputStream *output_stream;
|
|
gssize bytes_read;
|
|
GError *error = NULL;
|
|
|
|
bytes_read = g_input_stream_read_finish (
|
|
input_stream, result, &error);
|
|
|
|
if (error) {
|
|
image_load_context_free (load_context);
|
|
return;
|
|
}
|
|
|
|
if (bytes_read == 0) {
|
|
image_load_finish (load_context);
|
|
return;
|
|
}
|
|
|
|
output_stream = load_context->output_stream;
|
|
load_context->bytes_read = bytes_read;
|
|
|
|
g_output_stream_write_async (
|
|
output_stream,
|
|
load_context->buffer,
|
|
load_context->bytes_read,
|
|
G_PRIORITY_DEFAULT, NULL,
|
|
(GAsyncReadyCallback) image_load_write_cb,
|
|
load_context);
|
|
}
|
|
|
|
static void
|
|
image_load_file_read_cb (GFile *file,
|
|
GAsyncResult *result,
|
|
LoadContext *load_context)
|
|
{
|
|
GFileInputStream *input_stream;
|
|
GOutputStream *output_stream;
|
|
GError *error = NULL;
|
|
|
|
/* Input stream might be NULL, so don't use cast macro. */
|
|
input_stream = g_file_read_finish (file, result, &error);
|
|
load_context->input_stream = (GInputStream *) input_stream;
|
|
|
|
if (error) {
|
|
image_load_context_free (load_context);
|
|
return;
|
|
}
|
|
|
|
/* Load the contents into a GMemoryOutputStream. */
|
|
output_stream = g_memory_output_stream_new (
|
|
NULL, 0, g_realloc, g_free);
|
|
|
|
load_context->output_stream = output_stream;
|
|
|
|
g_input_stream_read_async (
|
|
load_context->input_stream,
|
|
load_context->buffer,
|
|
sizeof (load_context->buffer),
|
|
G_PRIORITY_DEFAULT, NULL,
|
|
(GAsyncReadyCallback) image_load_stream_read_cb,
|
|
load_context);
|
|
}
|
|
|
|
static void
|
|
image_load_query_info_cb (GFile *file,
|
|
GAsyncResult *result,
|
|
LoadContext *load_context)
|
|
{
|
|
GFileInfo *file_info;
|
|
GError *error = NULL;
|
|
|
|
file_info = g_file_query_info_finish (file, result, &error);
|
|
if (error) {
|
|
image_load_context_free (load_context);
|
|
return;
|
|
}
|
|
|
|
load_context->content_type = g_file_info_get_content_type (file_info);
|
|
load_context->total_num_bytes = g_file_info_get_size (file_info);
|
|
load_context->filename = g_file_info_get_name (file_info);
|
|
|
|
g_file_read_async (
|
|
file, G_PRIORITY_DEFAULT,
|
|
NULL, (GAsyncReadyCallback)
|
|
image_load_file_read_cb, load_context);
|
|
}
|
|
|
|
static void
|
|
image_load_and_insert_async (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *element,
|
|
const gchar *uri)
|
|
{
|
|
LoadContext *load_context;
|
|
GFile *file;
|
|
|
|
g_return_if_fail (uri && *uri);
|
|
|
|
file = g_file_new_for_uri (uri);
|
|
g_return_if_fail (file != NULL);
|
|
|
|
load_context = image_load_context_new (selection);
|
|
load_context->file = file;
|
|
load_context->element = element;
|
|
|
|
g_file_query_info_async (
|
|
file, "standard::*",
|
|
G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
|
|
NULL, (GAsyncReadyCallback)
|
|
image_load_query_info_cb, load_context);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_insert_image:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @image_uri: an URI of the source image
|
|
*
|
|
* Inserts image at current cursor position using @image_uri as source. When a
|
|
* text range is selected, it will be replaced by the image.
|
|
*/
|
|
void
|
|
e_html_editor_selection_insert_image (EHTMLEditorSelection *selection,
|
|
const gchar *image_uri)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (image_uri != NULL);
|
|
|
|
if (is_in_html_mode (selection)) {
|
|
if (strstr (image_uri, ";base64,")) {
|
|
if (g_str_has_prefix (image_uri, "data:"))
|
|
insert_base64_image (selection, image_uri, "", "");
|
|
if (strstr (image_uri, ";data")) {
|
|
const gchar *base64_data = strstr (image_uri, ";") + 1;
|
|
gchar *filename;
|
|
glong filename_length;
|
|
|
|
filename_length =
|
|
g_utf8_strlen (image_uri, -1) -
|
|
g_utf8_strlen (base64_data, -1) - 1;
|
|
filename = g_strndup (image_uri, filename_length);
|
|
|
|
insert_base64_image (selection, base64_data, filename, "");
|
|
g_free (filename);
|
|
}
|
|
} else
|
|
image_load_and_insert_async (selection, NULL, image_uri);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_replace_image_src:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @element: #WebKitDOMElement element
|
|
* @image_uri: an URI of the source image
|
|
*
|
|
* If given @element is image we will replace the src attribute of it with base64
|
|
* data from given @image_uri. Otherwise we will set the base64 data to
|
|
* the background attribute of given @element.
|
|
*/
|
|
void
|
|
e_html_editor_selection_replace_image_src (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *element,
|
|
const gchar *image_uri)
|
|
{
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
g_return_if_fail (image_uri != NULL);
|
|
g_return_if_fail (element && WEBKIT_DOM_IS_ELEMENT (element));
|
|
|
|
if (strstr (image_uri, ";base64,")) {
|
|
if (g_str_has_prefix (image_uri, "data:"))
|
|
replace_base64_image_src (
|
|
selection, element, image_uri, "", "");
|
|
if (strstr (image_uri, ";data")) {
|
|
const gchar *base64_data = strstr (image_uri, ";") + 1;
|
|
gchar *filename;
|
|
glong filename_length;
|
|
|
|
filename_length =
|
|
g_utf8_strlen (image_uri, -1) -
|
|
g_utf8_strlen (base64_data, -1) - 1;
|
|
filename = g_strndup (image_uri, filename_length);
|
|
|
|
replace_base64_image_src (
|
|
selection, element, base64_data, filename, "");
|
|
g_free (filename);
|
|
}
|
|
} else
|
|
image_load_and_insert_async (selection, element, image_uri);
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_move_caret_into_element (WebKitDOMDocument *document,
|
|
WebKitDOMElement *element,
|
|
gboolean to_start)
|
|
{
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *new_range;
|
|
|
|
if (!element)
|
|
return;
|
|
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
new_range = webkit_dom_document_create_range (document);
|
|
|
|
webkit_dom_range_select_node_contents (
|
|
new_range, WEBKIT_DOM_NODE (element), NULL);
|
|
webkit_dom_range_collapse (new_range, to_start, NULL);
|
|
webkit_dom_dom_selection_remove_all_ranges (dom_selection);
|
|
webkit_dom_dom_selection_add_range (dom_selection, new_range);
|
|
g_object_unref (new_range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
}
|
|
|
|
static gint
|
|
find_where_to_break_line (WebKitDOMNode *node,
|
|
gint max_len,
|
|
gint word_wrap_length)
|
|
{
|
|
gchar *str, *text_start;
|
|
gunichar uc;
|
|
gint pos;
|
|
gint last_space = 0;
|
|
gint length;
|
|
gint ret_val = 0;
|
|
gchar* position = NULL;
|
|
|
|
text_start = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
|
|
length = g_utf8_strlen (text_start, -1);
|
|
|
|
pos = 1;
|
|
last_space = 0;
|
|
str = text_start;
|
|
do {
|
|
uc = g_utf8_get_char (str);
|
|
if (!uc) {
|
|
ret_val = pos <= max_len ? pos : last_space > 0 ? last_space - 1 : 0;
|
|
goto out;
|
|
}
|
|
|
|
/* If last_space is zero then the word is longer than
|
|
* word_wrap_length characters, so continue until we find
|
|
* a space */
|
|
if ((pos > max_len) && (last_space > 0)) {
|
|
if (last_space > word_wrap_length) {
|
|
ret_val = last_space > 0 ? last_space - 1 : 0;
|
|
goto out;
|
|
}
|
|
|
|
if (last_space > max_len) {
|
|
if (g_unichar_isspace (g_utf8_get_char (text_start)))
|
|
ret_val = 1;
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (last_space == max_len - 1) {
|
|
uc = g_utf8_get_char (str);
|
|
if (g_unichar_isspace (uc) || str[0] == '-')
|
|
last_space++;
|
|
}
|
|
|
|
ret_val = last_space > 0 ? last_space - 1 : 0;
|
|
goto out;
|
|
}
|
|
|
|
if (g_unichar_isspace (uc) || str[0] == '-')
|
|
last_space = pos;
|
|
|
|
pos += 1;
|
|
str = g_utf8_next_char (str);
|
|
} while (*str);
|
|
|
|
if (max_len <= length)
|
|
position = g_utf8_offset_to_pointer (text_start, max_len);
|
|
|
|
if (position && g_unichar_isspace (g_utf8_get_char (position))) {
|
|
ret_val = max_len;
|
|
} else {
|
|
if (last_space == 0) {
|
|
/* If word is longer than word_wrap_length, we have to
|
|
* split at maximal given length. */
|
|
ret_val = max_len;
|
|
} else if (last_space < max_len) {
|
|
ret_val = last_space > 0 ? last_space - 1 : 0;
|
|
} else {
|
|
if (length > word_wrap_length)
|
|
ret_val = last_space > 0 ? last_space - 1 : 0;
|
|
else
|
|
ret_val = 0;
|
|
}
|
|
}
|
|
|
|
out:
|
|
g_free (text_start);
|
|
|
|
/* No space found, split at max_len. */
|
|
if (ret_val == 0)
|
|
ret_val = max_len;
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
static void
|
|
mark_and_remove_leading_space (WebKitDOMDocument *document,
|
|
WebKitDOMNode *node)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
element = webkit_dom_document_create_element (document, "SPAN", NULL);
|
|
webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL);
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
node,
|
|
NULL);
|
|
webkit_dom_character_data_replace_data (
|
|
WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
|
|
}
|
|
|
|
static WebKitDOMElement *
|
|
wrap_lines (EHTMLEditorSelection *selection,
|
|
WebKitDOMNode *block,
|
|
WebKitDOMDocument *document,
|
|
gboolean remove_all_br,
|
|
gint word_wrap_length)
|
|
{
|
|
WebKitDOMNode *node, *start_node, *block_clone;
|
|
WebKitDOMElement *element;
|
|
guint line_length;
|
|
gulong length_left;
|
|
gchar *text_content;
|
|
|
|
if (selection) {
|
|
gint ii, length;
|
|
WebKitDOMDocumentFragment *fragment;
|
|
WebKitDOMNodeList *list;
|
|
WebKitDOMRange *range;
|
|
|
|
range = html_editor_selection_get_current_range (selection);
|
|
fragment = webkit_dom_range_clone_contents (range, NULL);
|
|
g_object_unref (range);
|
|
|
|
/* Select all BR elements or just ours that are used for wrapping.
|
|
* We are not removing user BR elements when this function is activated
|
|
* from Format->Wrap Lines action */
|
|
list = webkit_dom_document_fragment_query_selector_all (
|
|
fragment,
|
|
remove_all_br ? "br" : "br.-x-evo-wrap-br",
|
|
NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
/* And remove them */
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, length);
|
|
remove_node (node);
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
list = webkit_dom_document_fragment_query_selector_all (
|
|
fragment, "span[data-hidden-space]", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *hidden_space_node;
|
|
|
|
hidden_space_node = webkit_dom_node_list_item (list, ii);
|
|
webkit_dom_html_element_set_outer_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL);
|
|
g_object_unref (hidden_space_node);
|
|
}
|
|
g_object_unref (list);
|
|
|
|
node = WEBKIT_DOM_NODE (fragment);
|
|
start_node = node;
|
|
} else {
|
|
if (!webkit_dom_node_has_child_nodes (block))
|
|
return WEBKIT_DOM_ELEMENT (block);
|
|
|
|
block_clone = webkit_dom_node_clone_node (block, TRUE);
|
|
element = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block_clone),
|
|
"span#-x-evo-caret-position",
|
|
NULL);
|
|
|
|
/* When we wrap, we are wrapping just the text after caret, text
|
|
* before the caret is already wrapped, so unwrap the text after
|
|
* the caret position */
|
|
element = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block_clone),
|
|
"span#-x-evo-selection-end-marker",
|
|
NULL);
|
|
|
|
if (element) {
|
|
WebKitDOMNode *nd = WEBKIT_DOM_NODE (element);
|
|
|
|
while (nd) {
|
|
WebKitDOMNode *next_nd = webkit_dom_node_get_next_sibling (nd);
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (nd)) {
|
|
if (remove_all_br)
|
|
remove_node (nd);
|
|
else if (element_has_class (WEBKIT_DOM_ELEMENT (nd), "-x-evo-wrap-br"))
|
|
remove_node (nd);
|
|
}
|
|
nd = next_nd;
|
|
}
|
|
}
|
|
|
|
webkit_dom_node_normalize (block_clone);
|
|
node = webkit_dom_node_get_first_child (block_clone);
|
|
if (node) {
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
if (g_strcmp0 ("\n", text_content) == 0)
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
g_free (text_content);
|
|
}
|
|
|
|
if (block_clone && WEBKIT_DOM_IS_ELEMENT (block_clone)) {
|
|
gint ii, length;
|
|
WebKitDOMNodeList *list;
|
|
|
|
list = webkit_dom_element_query_selector_all (
|
|
WEBKIT_DOM_ELEMENT (block_clone), "span[data-hidden-space]", NULL);
|
|
length = webkit_dom_node_list_get_length (list);
|
|
for (ii = 0; ii < length; ii++) {
|
|
WebKitDOMNode *hidden_space_node;
|
|
|
|
hidden_space_node = webkit_dom_node_list_item (list, ii);
|
|
webkit_dom_html_element_set_outer_text (
|
|
WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL);
|
|
g_object_unref (hidden_space_node);
|
|
}
|
|
g_object_unref (list);
|
|
}
|
|
|
|
/* We have to start from the end of the last wrapped line */
|
|
element = webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block_clone),
|
|
"span#-x-evo-selection-start-marker",
|
|
NULL);
|
|
|
|
if (element) {
|
|
WebKitDOMNode *nd = WEBKIT_DOM_NODE (element);
|
|
|
|
while ((nd = webkit_dom_node_get_previous_sibling (nd))) {
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (nd)) {
|
|
element = WEBKIT_DOM_ELEMENT (nd);
|
|
break;
|
|
} else
|
|
element = NULL;
|
|
}
|
|
}
|
|
|
|
if (element) {
|
|
node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
|
|
start_node = block_clone;
|
|
} else
|
|
start_node = node;
|
|
}
|
|
|
|
line_length = 0;
|
|
while (node) {
|
|
gint offset = 0;
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (node)) {
|
|
const gchar *newline;
|
|
WebKitDOMNode *next_sibling;
|
|
|
|
/* If there is temporary hidden space we remove it */
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
|
|
if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE))
|
|
webkit_dom_character_data_delete_data (
|
|
WEBKIT_DOM_CHARACTER_DATA (node),
|
|
0,
|
|
1,
|
|
NULL);
|
|
if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE))
|
|
webkit_dom_character_data_delete_data (
|
|
WEBKIT_DOM_CHARACTER_DATA (node),
|
|
g_utf8_strlen (text_content, -1) - 1,
|
|
1,
|
|
NULL);
|
|
g_free (text_content);
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
}
|
|
newline = g_strstr_len (text_content, -1, "\n");
|
|
|
|
next_sibling = node;
|
|
while (newline) {
|
|
next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (next_sibling),
|
|
g_utf8_pointer_to_offset (text_content, newline),
|
|
NULL));
|
|
|
|
if (!next_sibling)
|
|
break;
|
|
|
|
element = webkit_dom_document_create_element (
|
|
document, "BR", NULL);
|
|
element_add_class (element, "-x-evo-temp-wrap-text-br");
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (next_sibling),
|
|
WEBKIT_DOM_NODE (element),
|
|
next_sibling,
|
|
NULL);
|
|
|
|
g_free (text_content);
|
|
|
|
text_content = webkit_dom_node_get_text_content (next_sibling);
|
|
if (g_str_has_prefix (text_content, "\n")) {
|
|
webkit_dom_character_data_delete_data (
|
|
WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL);
|
|
g_free (text_content);
|
|
text_content =
|
|
webkit_dom_node_get_text_content (next_sibling);
|
|
}
|
|
newline = g_strstr_len (text_content, -1, "\n");
|
|
}
|
|
g_free (text_content);
|
|
} else {
|
|
if (is_selection_position_node (node)) {
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
continue;
|
|
}
|
|
|
|
/* If element is ANCHOR we wrap it separately */
|
|
if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
|
|
glong anchor_length;
|
|
WebKitDOMNode *next_sibling;
|
|
|
|
text_content = webkit_dom_node_get_text_content (node);
|
|
anchor_length = g_utf8_strlen (text_content, -1);
|
|
g_free (text_content);
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (node);
|
|
/* If the anchor doesn't fit on the line wrap after it */
|
|
if (anchor_length > word_wrap_length) {
|
|
WebKitDOMNode *inner_node;
|
|
|
|
while ((inner_node = webkit_dom_node_get_first_child (node))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
inner_node,
|
|
next_sibling,
|
|
NULL);
|
|
}
|
|
next_sibling = webkit_dom_node_get_next_sibling (node);
|
|
remove_node (node);
|
|
node = next_sibling;
|
|
continue;
|
|
}
|
|
|
|
if (line_length + anchor_length > word_wrap_length) {
|
|
if (webkit_dom_node_get_previous_sibling (node)) {
|
|
element = webkit_dom_document_create_element (
|
|
document, "BR", NULL);
|
|
element_add_class (element, "-x-evo-wrap-br");
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
node,
|
|
NULL);
|
|
}
|
|
line_length = anchor_length;
|
|
} else
|
|
line_length += anchor_length;
|
|
|
|
node = next_sibling;
|
|
continue;
|
|
}
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span")) {
|
|
WebKitDOMNode *prev_sibling;
|
|
|
|
prev_sibling = webkit_dom_node_get_previous_sibling (node);
|
|
if (prev_sibling && WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
|
|
element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Applet-tab-span"))
|
|
line_length += TAB_LENGTH;
|
|
else
|
|
line_length += TAB_LENGTH - line_length % TAB_LENGTH;
|
|
}
|
|
/* When we are not removing user-entered BR elements (lines wrapped by user),
|
|
* we need to skip those elements */
|
|
if (!remove_all_br && WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) {
|
|
if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
|
|
line_length = 0;
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
continue;
|
|
}
|
|
}
|
|
goto next_node;
|
|
}
|
|
|
|
/* If length of this node + what we already have is still less
|
|
* then word_wrap_length characters, then just join it and continue to next
|
|
* node */
|
|
length_left = webkit_dom_character_data_get_length (
|
|
WEBKIT_DOM_CHARACTER_DATA (node));
|
|
|
|
if ((length_left + line_length) < word_wrap_length) {
|
|
line_length += length_left;
|
|
goto next_node;
|
|
}
|
|
|
|
/* wrap until we have something */
|
|
while ((length_left + line_length) > word_wrap_length) {
|
|
gint max_length;
|
|
|
|
max_length = word_wrap_length - line_length;
|
|
if (max_length < 0)
|
|
max_length = word_wrap_length;
|
|
/* Find where we can line-break the node so that it
|
|
* effectively fills the rest of current row */
|
|
offset = find_where_to_break_line (
|
|
node, max_length, word_wrap_length);
|
|
|
|
element = webkit_dom_document_create_element (document, "BR", NULL);
|
|
element_add_class (element, "-x-evo-wrap-br");
|
|
|
|
if (offset > 0 && offset <= word_wrap_length) {
|
|
WebKitDOMNode *nd;
|
|
|
|
if (offset != length_left)
|
|
webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (node), offset, NULL);
|
|
|
|
nd = webkit_dom_node_get_next_sibling (node);
|
|
if (nd) {
|
|
gchar *nd_content;
|
|
|
|
nd_content = webkit_dom_node_get_text_content (nd);
|
|
if (nd_content && *nd_content) {
|
|
if (g_str_has_prefix (nd_content, " "))
|
|
mark_and_remove_leading_space (document, nd);
|
|
g_free (nd_content);
|
|
nd_content = webkit_dom_node_get_text_content (nd);
|
|
if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
|
|
remove_node (nd);
|
|
g_free (nd_content);
|
|
}
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
nd,
|
|
NULL);
|
|
|
|
node = webkit_dom_node_get_next_sibling (
|
|
WEBKIT_DOM_NODE (element));
|
|
} else {
|
|
webkit_dom_node_append_child (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
NULL);
|
|
}
|
|
} else if (offset > word_wrap_length) {
|
|
if (offset != length_left)
|
|
webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (node), offset + 1, NULL);
|
|
|
|
if (webkit_dom_node_get_next_sibling (node)) {
|
|
gchar *nd_content;
|
|
WebKitDOMNode *nd = webkit_dom_node_get_next_sibling (node);
|
|
|
|
nd = webkit_dom_node_get_next_sibling (node);
|
|
nd_content = webkit_dom_node_get_text_content (nd);
|
|
if (nd_content && *nd_content) {
|
|
if (g_str_has_prefix (nd_content, " "))
|
|
mark_and_remove_leading_space (document, nd);
|
|
g_free (nd_content);
|
|
nd_content = webkit_dom_node_get_text_content (nd);
|
|
if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
|
|
remove_node (nd);
|
|
g_free (nd_content);
|
|
}
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
nd,
|
|
NULL);
|
|
|
|
line_length = 0;
|
|
break;
|
|
} else {
|
|
node = WEBKIT_DOM_NODE (webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (node), word_wrap_length - line_length, NULL));
|
|
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
node,
|
|
NULL);
|
|
}
|
|
} else {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (element),
|
|
node,
|
|
NULL);
|
|
}
|
|
length_left = webkit_dom_character_data_get_length (
|
|
WEBKIT_DOM_CHARACTER_DATA (node));
|
|
|
|
line_length = 0;
|
|
}
|
|
line_length += length_left - offset;
|
|
next_node:
|
|
if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
|
|
line_length = 0;
|
|
|
|
/* Move to next node */
|
|
if (webkit_dom_node_has_child_nodes (node)) {
|
|
node = webkit_dom_node_get_first_child (node);
|
|
} else if (webkit_dom_node_get_next_sibling (node)) {
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
} else {
|
|
if (webkit_dom_node_is_equal_node (node, start_node))
|
|
break;
|
|
|
|
node = webkit_dom_node_get_parent_node (node);
|
|
if (node)
|
|
node = webkit_dom_node_get_next_sibling (node);
|
|
}
|
|
}
|
|
|
|
if (selection) {
|
|
gchar *html;
|
|
|
|
/* Create a wrapper DIV and put the processed content into it */
|
|
element = webkit_dom_document_create_element (document, "DIV", NULL);
|
|
element_add_class (element, "-x-evo-paragraph");
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (element),
|
|
WEBKIT_DOM_NODE (start_node),
|
|
NULL);
|
|
|
|
webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
|
|
/* Get HTML code of the processed content */
|
|
html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (element));
|
|
|
|
/* Overwrite the current selection by the processed content */
|
|
e_html_editor_selection_insert_html (selection, html);
|
|
|
|
g_free (html);
|
|
|
|
return NULL;
|
|
} else {
|
|
webkit_dom_node_normalize (block_clone);
|
|
|
|
node = webkit_dom_node_get_parent_node (block);
|
|
if (node) {
|
|
/* Replace block with wrapped one */
|
|
webkit_dom_node_replace_child (
|
|
node, block_clone, block, NULL);
|
|
}
|
|
|
|
return WEBKIT_DOM_ELEMENT (block_clone);
|
|
}
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_set_indented_style (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *element,
|
|
gint width)
|
|
{
|
|
gchar *style;
|
|
gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
|
|
|
|
webkit_dom_element_set_class_name (element, "-x-evo-indented");
|
|
|
|
if (is_in_html_mode (selection))
|
|
style = g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION);
|
|
else
|
|
style = g_strdup_printf (
|
|
"margin-left: %dch; word-wrap: normal; width: %dch;",
|
|
SPACES_PER_INDENTATION, word_wrap_length);
|
|
|
|
webkit_dom_element_set_attribute (element, "style", style, NULL);
|
|
g_free (style);
|
|
}
|
|
|
|
WebKitDOMElement *
|
|
e_html_editor_selection_get_indented_element (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
gint width)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
element = webkit_dom_document_create_element (document, "DIV", NULL);
|
|
e_html_editor_selection_set_indented_style (selection, element, width);
|
|
|
|
return element;
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_set_paragraph_style (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *element,
|
|
gint width,
|
|
gint offset,
|
|
const gchar *style_to_add)
|
|
{
|
|
EHTMLEditorSelectionAlignment alignment;
|
|
char *style = NULL;
|
|
gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
|
|
|
|
alignment = e_html_editor_selection_get_alignment (selection);
|
|
|
|
element_add_class (element, "-x-evo-paragraph");
|
|
element_add_class (element, get_css_alignment_value_class (alignment));
|
|
if (!is_in_html_mode (selection)) {
|
|
style = g_strdup_printf (
|
|
"width: %dch; word-wrap: normal; %s",
|
|
(word_wrap_length + offset), style_to_add);
|
|
} else {
|
|
if (*style_to_add)
|
|
style = g_strdup_printf ("%s", style_to_add);
|
|
}
|
|
if (style) {
|
|
webkit_dom_element_set_attribute (element, "style", style, NULL);
|
|
g_free (style);
|
|
}
|
|
}
|
|
|
|
WebKitDOMElement *
|
|
e_html_editor_selection_get_paragraph_element (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
gint width,
|
|
gint offset)
|
|
{
|
|
WebKitDOMElement *element;
|
|
|
|
element = webkit_dom_document_create_element (document, "DIV", NULL);
|
|
e_html_editor_selection_set_paragraph_style (selection, element, width, offset, "");
|
|
|
|
return element;
|
|
}
|
|
|
|
WebKitDOMElement *
|
|
e_html_editor_selection_put_node_into_paragraph (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document,
|
|
WebKitDOMNode *node,
|
|
gboolean with_input)
|
|
{
|
|
WebKitDOMRange *range;
|
|
WebKitDOMElement *container;
|
|
|
|
range = webkit_dom_document_create_range (document);
|
|
container = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0);
|
|
webkit_dom_range_select_node (range, node, NULL);
|
|
webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL);
|
|
/* We have to move caret position inside this container */
|
|
if (with_input)
|
|
add_selection_markers_into_element_end (document, container, NULL, NULL);
|
|
|
|
g_object_unref (range);
|
|
return container;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_wrap_lines:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Wraps all lines in current selection to be 71 characters long.
|
|
*/
|
|
void
|
|
e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
EHTMLEditorViewHistoryEvent *ev = NULL;
|
|
gboolean after_selection_end = FALSE, html_mode;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *block, *next_block;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
selection_start_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-start-marker", NULL);
|
|
selection_end_marker = webkit_dom_document_query_selector (
|
|
document, "span#-x-evo-selection-end-marker", NULL);
|
|
|
|
/* If the selection was not saved, move it into the first child of body */
|
|
if (!selection_start_marker || !selection_end_marker) {
|
|
WebKitDOMHTMLElement *body;
|
|
WebKitDOMNode *child;
|
|
|
|
body = webkit_dom_document_get_body (document);
|
|
child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
|
|
|
|
add_selection_markers_into_element_start (
|
|
document,
|
|
WEBKIT_DOM_ELEMENT (child),
|
|
&selection_start_marker,
|
|
&selection_end_marker);
|
|
}
|
|
|
|
if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
|
|
ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
|
|
ev->type = HISTORY_WRAP;
|
|
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->before.start.x,
|
|
&ev->before.start.y,
|
|
&ev->before.end.x,
|
|
&ev->before.end.y);
|
|
ev->data.style.from = 1;
|
|
ev->data.style.to = 1;
|
|
}
|
|
|
|
block = get_parent_block_node_from_child (
|
|
WEBKIT_DOM_NODE (selection_start_marker));
|
|
|
|
html_mode = e_html_editor_view_get_html_mode (view);
|
|
|
|
/* Process all blocks that are in the selection one by one */
|
|
while (block && !after_selection_end) {
|
|
gboolean quoted = FALSE;
|
|
gint citation_level, quote;
|
|
WebKitDOMElement *wrapped_paragraph;
|
|
|
|
if (webkit_dom_element_query_selector (
|
|
WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
|
|
quoted = TRUE;
|
|
remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
}
|
|
|
|
if (!html_mode)
|
|
remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
|
|
|
|
after_selection_end = webkit_dom_node_contains (
|
|
block, WEBKIT_DOM_NODE (selection_end_marker));
|
|
|
|
next_block = webkit_dom_node_get_next_sibling (block);
|
|
|
|
citation_level = get_citation_level (block);
|
|
quote = citation_level ? citation_level * 2 : 0;
|
|
|
|
wrapped_paragraph = e_html_editor_selection_wrap_paragraph_length (
|
|
selection, WEBKIT_DOM_ELEMENT (block), selection->priv->word_wrap_length - quote);
|
|
|
|
webkit_dom_element_set_attribute (
|
|
wrapped_paragraph, "data-user-wrapped", "", NULL);
|
|
|
|
if (quoted && !html_mode)
|
|
e_html_editor_view_quote_plain_text_element (view, wrapped_paragraph);
|
|
|
|
block = next_block;
|
|
}
|
|
|
|
if (ev) {
|
|
e_html_editor_selection_get_selection_coordinates (
|
|
selection,
|
|
&ev->after.start.x,
|
|
&ev->after.start.y,
|
|
&ev->after.end.x,
|
|
&ev->after.end.y);
|
|
e_html_editor_view_insert_new_history_event (view, ev);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
e_html_editor_view_force_spell_check_for_current_paragraph (view);
|
|
|
|
g_object_unref (view);
|
|
}
|
|
|
|
WebKitDOMElement *
|
|
e_html_editor_selection_wrap_paragraph_length (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *paragraph,
|
|
gint length)
|
|
{
|
|
WebKitDOMDocument *document;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
|
|
g_return_val_if_fail (length >= MINIMAL_PARAGRAPH_WIDTH, NULL);
|
|
|
|
document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (paragraph));
|
|
|
|
return wrap_lines (
|
|
NULL, WEBKIT_DOM_NODE (paragraph), document, FALSE, length);
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_wrap_paragraphs_in_document (EHTMLEditorSelection *selection,
|
|
WebKitDOMDocument *document)
|
|
{
|
|
WebKitDOMNodeList *list;
|
|
gint ii, length;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
/* Only wrap paragraphs that are inside the quoted content, others are
|
|
* wrapped by CSS. */
|
|
list = webkit_dom_document_query_selector_all (
|
|
document,
|
|
"blockquote[type=cite] > div.-x-evo-paragraph:not(#-x-evo-input-start)",
|
|
NULL);
|
|
|
|
length = webkit_dom_node_list_get_length (list);
|
|
|
|
for (ii = 0; ii < length; ii++) {
|
|
gint quote, citation_level;
|
|
WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
|
|
|
|
citation_level = get_citation_level (node);
|
|
quote = citation_level ? citation_level * 2 : 0;
|
|
|
|
if (node_is_list (node)) {
|
|
WebKitDOMNode *item = webkit_dom_node_get_first_child (node);
|
|
|
|
while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
|
|
e_html_editor_selection_wrap_paragraph_length (
|
|
selection,
|
|
WEBKIT_DOM_ELEMENT (item),
|
|
selection->priv->word_wrap_length - quote);
|
|
item = webkit_dom_node_get_next_sibling (item);
|
|
}
|
|
} else {
|
|
e_html_editor_selection_wrap_paragraph_length (
|
|
selection,
|
|
WEBKIT_DOM_ELEMENT (node),
|
|
selection->priv->word_wrap_length - quote);
|
|
}
|
|
g_object_unref (node);
|
|
}
|
|
g_object_unref (list);
|
|
}
|
|
|
|
WebKitDOMElement *
|
|
e_html_editor_selection_wrap_paragraph (EHTMLEditorSelection *selection,
|
|
WebKitDOMElement *paragraph)
|
|
{
|
|
gint indentation_level, citation_level, quote;
|
|
gint final_width, word_wrap_length, offset = 0;
|
|
|
|
g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
|
|
g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
|
|
|
|
word_wrap_length = selection->priv->word_wrap_length;
|
|
indentation_level = get_indentation_level (paragraph);
|
|
citation_level = get_citation_level (WEBKIT_DOM_NODE (paragraph));
|
|
|
|
if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph))) {
|
|
gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph));
|
|
indentation_level = 0;
|
|
|
|
if (list_level > 0)
|
|
offset = list_level * -SPACES_PER_LIST_LEVEL;
|
|
else
|
|
offset = -SPACES_PER_LIST_LEVEL;
|
|
}
|
|
|
|
quote = citation_level ? citation_level * 2 : 0;
|
|
|
|
final_width = word_wrap_length - quote + offset;
|
|
final_width -= SPACES_PER_INDENTATION * indentation_level;
|
|
|
|
return e_html_editor_selection_wrap_paragraph_length (
|
|
selection, WEBKIT_DOM_ELEMENT (paragraph), final_width);
|
|
}
|
|
|
|
static WebKitDOMNode *
|
|
in_empty_block_in_quoted_content (WebKitDOMNode *element)
|
|
{
|
|
WebKitDOMNode *first_child, *next_sibling;
|
|
|
|
first_child = webkit_dom_node_get_first_child (element);
|
|
if (!WEBKIT_DOM_IS_ELEMENT (first_child))
|
|
return NULL;
|
|
|
|
if (!element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted"))
|
|
return NULL;
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (first_child);
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling))
|
|
return next_sibling;
|
|
|
|
if (!WEBKIT_DOM_IS_ELEMENT (next_sibling))
|
|
return NULL;
|
|
|
|
if (!element_has_id (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-selection-start-marker"))
|
|
return NULL;
|
|
|
|
next_sibling = webkit_dom_node_get_next_sibling (next_sibling);
|
|
if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling))
|
|
return next_sibling;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_save:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Saves current cursor position or current selection range. The selection can
|
|
* be later restored by calling e_html_editor_selection_restore().
|
|
*
|
|
* Note that calling e_html_editor_selection_save() overwrites previously saved
|
|
* position.
|
|
*
|
|
* Note that this method inserts special markings into the HTML code that are
|
|
* used to later restore the selection. It can happen that by deleting some
|
|
* segments of the document some of the markings are deleted too. In that case
|
|
* restoring the selection by e_html_editor_selection_restore() can fail. Also by
|
|
* moving text segments (Cut & Paste) can result in moving the markings
|
|
* elsewhere, thus e_html_editor_selection_restore() will restore the selection
|
|
* incorrectly.
|
|
*
|
|
* It is recommended to use this method only when you are not planning to make
|
|
* bigger changes to content or structure of the document (formatting changes
|
|
* are usually OK).
|
|
*/
|
|
void
|
|
e_html_editor_selection_save (EHTMLEditorSelection *selection)
|
|
{
|
|
gboolean collapsed = FALSE;
|
|
glong offset, anchor_offset;
|
|
EHTMLEditorView *view;
|
|
WebKitWebView *web_view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMNode *container, *next_sibling, *marker_node;
|
|
WebKitDOMNode *split_node, *parent_node, *anchor;
|
|
WebKitDOMElement *start_marker = NULL, *end_marker = NULL;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
web_view = WEBKIT_WEB_VIEW (view);
|
|
|
|
document = webkit_web_view_get_dom_document (web_view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
g_object_unref (view);
|
|
|
|
/* First remove all markers (if present) */
|
|
remove_selection_markers (document);
|
|
|
|
if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
return;
|
|
}
|
|
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
if (!range) {
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
return;
|
|
}
|
|
|
|
anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection);
|
|
anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
|
|
|
|
collapsed = webkit_dom_range_get_collapsed (range, NULL);
|
|
start_marker = create_selection_marker (document, TRUE);
|
|
|
|
container = webkit_dom_range_get_start_container (range, NULL);
|
|
offset = webkit_dom_range_get_start_offset (range, NULL);
|
|
parent_node = webkit_dom_node_get_parent_node (container);
|
|
|
|
if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset)
|
|
webkit_dom_element_set_attribute (start_marker, "data-anchor", "", NULL);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) {
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_node_get_parent_node (
|
|
webkit_dom_node_get_parent_node (parent_node));
|
|
|
|
if ((next_sibling = in_empty_block_in_quoted_content (node))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (next_sibling),
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
next_sibling,
|
|
NULL);
|
|
goto insert_end_marker;
|
|
}
|
|
} else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) {
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_node_get_parent_node (parent_node);
|
|
if (offset == 0) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (node),
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
node,
|
|
NULL);
|
|
goto insert_end_marker;
|
|
}
|
|
}
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (container)) {
|
|
if (offset != 0) {
|
|
WebKitDOMText *split_text;
|
|
|
|
split_text = webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (container), offset, NULL);
|
|
split_node = WEBKIT_DOM_NODE (split_text);
|
|
} else {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
parent_node,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
container,
|
|
NULL);
|
|
goto insert_end_marker;
|
|
}
|
|
} else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_first_child (container),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
} else if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (container)) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_first_child (container),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
} else if (element_has_class (WEBKIT_DOM_ELEMENT (container), "-x-evo-resizable-wrapper")) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
parent_node,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_next_sibling (container),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
} else {
|
|
/* Insert the selection marker on the right position in
|
|
* an empty paragraph in the quoted content */
|
|
if ((next_sibling = in_empty_block_in_quoted_content (container))) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
next_sibling,
|
|
NULL);
|
|
goto insert_end_marker;
|
|
}
|
|
if (!webkit_dom_node_get_previous_sibling (container)) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_first_child (container),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
} else if (!webkit_dom_node_get_next_sibling (container)) {
|
|
WebKitDOMNode *tmp;
|
|
|
|
tmp = webkit_dom_node_get_last_child (container);
|
|
if (tmp && WEBKIT_DOM_IS_HTMLBR_ELEMENT (tmp))
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
tmp,
|
|
NULL);
|
|
else
|
|
marker_node = webkit_dom_node_append_child (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
} else {
|
|
if (webkit_dom_node_get_first_child (container)) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_first_child (container),
|
|
NULL);
|
|
goto insert_end_marker;
|
|
}
|
|
split_node = container;
|
|
}
|
|
}
|
|
|
|
/* Don't save selection straight into body */
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node))
|
|
goto out;
|
|
|
|
if (!split_node) {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (start_marker),
|
|
webkit_dom_node_get_first_child (
|
|
WEBKIT_DOM_NODE (container)),
|
|
NULL);
|
|
} else {
|
|
marker_node = WEBKIT_DOM_NODE (start_marker);
|
|
parent_node = webkit_dom_node_get_parent_node (split_node);
|
|
|
|
webkit_dom_node_insert_before (
|
|
parent_node, marker_node, split_node, NULL);
|
|
}
|
|
|
|
webkit_dom_node_normalize (parent_node);
|
|
|
|
insert_end_marker:
|
|
end_marker = create_selection_marker (document, FALSE);
|
|
|
|
if (collapsed) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker)),
|
|
WEBKIT_DOM_NODE (end_marker),
|
|
webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker)),
|
|
NULL);
|
|
goto out;
|
|
}
|
|
|
|
container = webkit_dom_range_get_end_container (range, NULL);
|
|
offset = webkit_dom_range_get_end_offset (range, NULL);
|
|
parent_node = webkit_dom_node_get_parent_node (container);
|
|
|
|
if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset)
|
|
webkit_dom_element_set_attribute (end_marker, "data-anchor", "", NULL);
|
|
|
|
if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) {
|
|
WebKitDOMNode *node;
|
|
|
|
node = webkit_dom_node_get_parent_node (
|
|
webkit_dom_node_get_parent_node (parent_node));
|
|
|
|
if ((next_sibling = in_empty_block_in_quoted_content (node))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (next_sibling),
|
|
WEBKIT_DOM_NODE (end_marker),
|
|
next_sibling,
|
|
NULL);
|
|
} else {
|
|
webkit_dom_node_insert_before (
|
|
node,
|
|
WEBKIT_DOM_NODE (end_marker),
|
|
webkit_dom_node_get_next_sibling (
|
|
webkit_dom_node_get_parent_node (parent_node)),
|
|
NULL);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (WEBKIT_DOM_IS_TEXT (container)) {
|
|
if (offset != 0) {
|
|
WebKitDOMText *split_text;
|
|
|
|
split_text = webkit_dom_text_split_text (
|
|
WEBKIT_DOM_TEXT (container), offset, NULL);
|
|
split_node = WEBKIT_DOM_NODE (split_text);
|
|
} else {
|
|
marker_node = webkit_dom_node_insert_before (
|
|
parent_node, WEBKIT_DOM_NODE (end_marker), container, NULL);
|
|
goto check;
|
|
|
|
}
|
|
} else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
|
|
webkit_dom_node_append_child (
|
|
container, WEBKIT_DOM_NODE (end_marker), NULL);
|
|
goto out;
|
|
} else {
|
|
/* Insert the selection marker on the right position in
|
|
* an empty paragraph in the quoted content */
|
|
if ((next_sibling = in_empty_block_in_quoted_content (container))) {
|
|
webkit_dom_node_insert_before (
|
|
container,
|
|
WEBKIT_DOM_NODE (end_marker),
|
|
next_sibling,
|
|
NULL);
|
|
goto out;
|
|
}
|
|
if (!webkit_dom_node_get_previous_sibling (container)) {
|
|
split_node = parent_node;
|
|
} else if (!webkit_dom_node_get_next_sibling (container)) {
|
|
split_node = parent_node;
|
|
split_node = webkit_dom_node_get_next_sibling (split_node);
|
|
} else
|
|
split_node = container;
|
|
}
|
|
|
|
/* Don't save selection straight into body */
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) {
|
|
remove_node (WEBKIT_DOM_NODE (start_marker));
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
return;
|
|
}
|
|
|
|
marker_node = WEBKIT_DOM_NODE (end_marker);
|
|
|
|
if (split_node) {
|
|
parent_node = webkit_dom_node_get_parent_node (split_node);
|
|
|
|
if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) {
|
|
if (offset == 0)
|
|
webkit_dom_node_insert_before (
|
|
split_node,
|
|
marker_node,
|
|
webkit_dom_node_get_first_child (split_node),
|
|
NULL);
|
|
else
|
|
webkit_dom_node_append_child (
|
|
webkit_dom_node_get_previous_sibling (split_node),
|
|
marker_node,
|
|
NULL);
|
|
} else
|
|
webkit_dom_node_insert_before (
|
|
parent_node, marker_node, split_node, NULL);
|
|
} else {
|
|
WebKitDOMNode *first_child;
|
|
|
|
first_child = webkit_dom_node_get_first_child (container);
|
|
if (offset == 0 && WEBKIT_DOM_IS_TEXT (first_child))
|
|
webkit_dom_node_insert_before (
|
|
WEBKIT_DOM_NODE (container), marker_node, webkit_dom_node_get_first_child (container), NULL);
|
|
else
|
|
webkit_dom_node_append_child (
|
|
WEBKIT_DOM_NODE (container), marker_node, NULL);
|
|
}
|
|
|
|
webkit_dom_node_normalize (parent_node);
|
|
|
|
check:
|
|
if ((next_sibling = webkit_dom_node_get_next_sibling (marker_node))) {
|
|
if (!WEBKIT_DOM_IS_ELEMENT (next_sibling))
|
|
next_sibling = webkit_dom_node_get_next_sibling (next_sibling);
|
|
/* If the selection is collapsed ensure that the selection start marker
|
|
* is before the end marker */
|
|
if (next_sibling && webkit_dom_node_is_same_node (next_sibling, WEBKIT_DOM_NODE (start_marker))) {
|
|
webkit_dom_node_insert_before (
|
|
webkit_dom_node_get_parent_node (marker_node),
|
|
next_sibling,
|
|
marker_node,
|
|
NULL);
|
|
}
|
|
}
|
|
out:
|
|
if (!collapsed) {
|
|
if (start_marker && end_marker) {
|
|
webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (start_marker), NULL);
|
|
webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (end_marker), NULL);
|
|
} else {
|
|
g_warn_if_reached ();
|
|
}
|
|
|
|
webkit_dom_dom_selection_remove_all_ranges (dom_selection);
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
}
|
|
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_restore:
|
|
* @selection: an #EHTMLEditorSelection
|
|
*
|
|
* Restores cursor position or selection range that was saved by
|
|
* e_html_editor_selection_save().
|
|
*
|
|
* Note that calling this function without calling e_html_editor_selection_save()
|
|
* before is a programming error and the behavior is undefined.
|
|
*/
|
|
void
|
|
e_html_editor_selection_restore (EHTMLEditorSelection *selection)
|
|
{
|
|
EHTMLEditorView *view;
|
|
gboolean start_is_anchor = FALSE;
|
|
glong offset;
|
|
WebKitWebView *web_view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMElement *marker;
|
|
WebKitDOMNode *selection_start_marker, *selection_end_marker;
|
|
WebKitDOMNode *parent_start, *parent_end, *anchor;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
web_view = WEBKIT_WEB_VIEW (view);
|
|
|
|
document = webkit_web_view_get_dom_document (web_view);
|
|
g_object_unref (view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
|
|
g_object_unref (dom_window);
|
|
if (!range) {
|
|
WebKitDOMHTMLElement *body;
|
|
|
|
range = webkit_dom_document_create_range (document);
|
|
body = webkit_dom_document_get_body (document);
|
|
|
|
webkit_dom_range_select_node_contents (range, WEBKIT_DOM_NODE (body), NULL);
|
|
webkit_dom_range_collapse (range, TRUE, NULL);
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
}
|
|
|
|
selection_start_marker = webkit_dom_range_get_start_container (range, NULL);
|
|
if (selection_start_marker) {
|
|
gboolean ok = FALSE;
|
|
selection_start_marker =
|
|
webkit_dom_node_get_next_sibling (selection_start_marker);
|
|
|
|
ok = is_selection_position_node (selection_start_marker);
|
|
|
|
if (ok) {
|
|
ok = FALSE;
|
|
if (webkit_dom_range_get_collapsed (range, NULL)) {
|
|
selection_end_marker = webkit_dom_node_get_next_sibling (
|
|
selection_start_marker);
|
|
|
|
ok = is_selection_position_node (selection_end_marker);
|
|
if (ok) {
|
|
parent_start = webkit_dom_node_get_parent_node (selection_end_marker);
|
|
|
|
remove_node (selection_start_marker);
|
|
remove_node (selection_end_marker);
|
|
|
|
webkit_dom_node_normalize (parent_start);
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g_object_unref (range);
|
|
range = webkit_dom_document_create_range (document);
|
|
if (!range) {
|
|
g_object_unref (dom_selection);
|
|
return;
|
|
}
|
|
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (!marker) {
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
if (marker)
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (range);
|
|
return;
|
|
}
|
|
|
|
start_is_anchor = webkit_dom_element_has_attribute (marker, "data-anchor");
|
|
parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker));
|
|
|
|
webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (marker), NULL);
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-end-marker");
|
|
if (!marker) {
|
|
marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (marker)
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (range);
|
|
return;
|
|
}
|
|
|
|
parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker));
|
|
webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (marker), NULL);
|
|
remove_node (WEBKIT_DOM_NODE (marker));
|
|
|
|
webkit_dom_dom_selection_remove_all_ranges (dom_selection);
|
|
if (webkit_dom_node_is_same_node (parent_start, parent_end))
|
|
webkit_dom_node_normalize (parent_start);
|
|
else {
|
|
webkit_dom_node_normalize (parent_start);
|
|
webkit_dom_node_normalize (parent_end);
|
|
}
|
|
|
|
if (start_is_anchor) {
|
|
anchor = webkit_dom_range_get_end_container (range, NULL);
|
|
offset = webkit_dom_range_get_end_offset (range, NULL);
|
|
|
|
webkit_dom_range_collapse (range, TRUE, NULL);
|
|
} else {
|
|
anchor = webkit_dom_range_get_start_container (range, NULL);
|
|
offset = webkit_dom_range_get_start_offset (range, NULL);
|
|
|
|
webkit_dom_range_collapse (range, FALSE, NULL);
|
|
}
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
webkit_dom_dom_selection_extend (dom_selection, anchor, offset, NULL);
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_set_on_point (EHTMLEditorSelection *selection,
|
|
guint x,
|
|
guint y)
|
|
{
|
|
EHTMLEditorView *view;
|
|
WebKitDOMRange *range;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
range = webkit_dom_document_caret_range_from_point (document, x, y);
|
|
webkit_dom_dom_selection_remove_all_ranges (dom_selection);
|
|
webkit_dom_dom_selection_add_range (dom_selection, range);
|
|
g_object_unref (range);
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
}
|
|
|
|
static void
|
|
html_editor_selection_modify (EHTMLEditorSelection *selection,
|
|
const gchar *alter,
|
|
gboolean forward,
|
|
EHTMLEditorSelectionGranularity granularity)
|
|
{
|
|
EHTMLEditorView *view;
|
|
WebKitWebView *web_view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMDOMSelection *dom_selection;
|
|
const gchar *granularity_str = NULL;
|
|
|
|
g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
g_return_if_fail (view != NULL);
|
|
|
|
web_view = WEBKIT_WEB_VIEW (view);
|
|
|
|
document = webkit_web_view_get_dom_document (web_view);
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
dom_selection = webkit_dom_dom_window_get_selection (dom_window);
|
|
|
|
switch (granularity) {
|
|
case E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER:
|
|
granularity_str = "character";
|
|
break;
|
|
case E_HTML_EDITOR_SELECTION_GRANULARITY_WORD:
|
|
granularity_str = "word";
|
|
break;
|
|
}
|
|
|
|
if (granularity_str) {
|
|
webkit_dom_dom_selection_modify (
|
|
dom_selection, alter,
|
|
forward ? "forward" : "backward",
|
|
granularity_str);
|
|
}
|
|
|
|
g_object_unref (dom_selection);
|
|
g_object_unref (dom_window);
|
|
g_object_unref (view);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_extend:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @forward: whether to extend selection forward or backward
|
|
* @granularity: granularity of the extension
|
|
*
|
|
* Extends current selection in given direction by given granularity.
|
|
*/
|
|
void
|
|
e_html_editor_selection_extend (EHTMLEditorSelection *selection,
|
|
gboolean forward,
|
|
EHTMLEditorSelectionGranularity granularity)
|
|
{
|
|
html_editor_selection_modify (selection, "extend", forward, granularity);
|
|
}
|
|
|
|
/**
|
|
* e_html_editor_selection_move:
|
|
* @selection: an #EHTMLEditorSelection
|
|
* @forward: whether to move the selection forward or backward
|
|
* @granularity: granularity of the movement
|
|
*
|
|
* Moves current selection in given direction by given granularity
|
|
*/
|
|
void
|
|
e_html_editor_selection_move (EHTMLEditorSelection *selection,
|
|
gboolean forward,
|
|
EHTMLEditorSelectionGranularity granularity)
|
|
{
|
|
html_editor_selection_modify (selection, "move", forward, granularity);
|
|
}
|
|
|
|
void
|
|
e_html_editor_selection_scroll_to_caret (EHTMLEditorSelection *selection)
|
|
{
|
|
glong element_top, element_left;
|
|
glong window_top, window_left, window_right, window_bottom;
|
|
EHTMLEditorView *view;
|
|
WebKitDOMDocument *document;
|
|
WebKitDOMDOMWindow *dom_window;
|
|
WebKitDOMElement *selection_start_marker;
|
|
|
|
e_html_editor_selection_save (selection);
|
|
|
|
view = e_html_editor_selection_ref_html_editor_view (selection);
|
|
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
|
|
g_object_unref (view);
|
|
|
|
selection_start_marker = webkit_dom_document_get_element_by_id (
|
|
document, "-x-evo-selection-start-marker");
|
|
if (!selection_start_marker)
|
|
return;
|
|
|
|
dom_window = webkit_dom_document_get_default_view (document);
|
|
|
|
window_top = webkit_dom_dom_window_get_scroll_y (dom_window);
|
|
window_left = webkit_dom_dom_window_get_scroll_x (dom_window);
|
|
window_bottom = window_top + webkit_dom_dom_window_get_inner_height (dom_window);
|
|
window_right = window_left + webkit_dom_dom_window_get_inner_width (dom_window);
|
|
|
|
element_left = webkit_dom_element_get_offset_left (selection_start_marker);
|
|
element_top = webkit_dom_element_get_offset_top (selection_start_marker);
|
|
|
|
/* Check if caret is inside viewport, if not move to it */
|
|
if (!(element_top >= window_top && element_top <= window_bottom &&
|
|
element_left >= window_left && element_left <= window_right)) {
|
|
webkit_dom_element_scroll_into_view (selection_start_marker, TRUE);
|
|
}
|
|
|
|
e_html_editor_selection_restore (selection);
|
|
|
|
g_object_unref (dom_window);
|
|
}
|