aboutsummaryrefslogtreecommitdiffstats
path: root/composer
diff options
context:
space:
mode:
authorTomas Popela <tpopela@redhat.com>2014-06-09 22:32:25 +0800
committerTomas Popela <tpopela@redhat.com>2014-06-09 22:32:25 +0800
commit8650fb139a9143f04615de74ff569bce3e0c4ce3 (patch)
tree89a41d08f179a5359b8eaee0c9344b8a5bf07cb3 /composer
parent04b7c97275ae420dca43f3e65c2ef54d02f01bdd (diff)
downloadgsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar.gz
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar.bz2
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar.lz
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar.xz
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.tar.zst
gsoc2013-evolution-8650fb139a9143f04615de74ff569bce3e0c4ce3.zip
Bug 540362: [webkit-composer] Use webkit for composer
Merge wip/webkit-composer branch into master.
Diffstat (limited to 'composer')
-rw-r--r--composer/Makefile.am8
-rw-r--r--composer/e-composer-actions.c81
-rw-r--r--composer/e-composer-actions.h4
-rw-r--r--composer/e-composer-activity.c187
-rw-r--r--composer/e-composer-activity.h64
-rw-r--r--composer/e-composer-private.c858
-rw-r--r--composer/e-composer-private.h25
-rw-r--r--composer/e-composer-spell-header.c13
-rw-r--r--composer/e-composer-spell-header.h3
-rw-r--r--composer/e-msg-composer.c1318
-rw-r--r--composer/e-msg-composer.h30
11 files changed, 1280 insertions, 1311 deletions
diff --git a/composer/Makefile.am b/composer/Makefile.am
index aa7886403e..86a411a07e 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -10,7 +10,6 @@ evolution_mail_composer_includedir = $(privincludedir)/composer
evolution_mail_composer_include_HEADERS = \
e-composer-actions.h \
- e-composer-activity.h \
e-composer-common.h \
e-composer-from-header.h \
e-composer-header-table.h \
@@ -36,13 +35,12 @@ libevolution_mail_composer_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"composer\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_mail_composer_la_SOURCES = \
$(evolution_mail_composer_include_HEADERS) \
e-composer-actions.c \
- e-composer-activity.c \
e-composer-from-header.c \
e-composer-header-table.c \
e-composer-header.c \
@@ -63,7 +61,7 @@ libevolution_mail_composer_la_LIBADD = \
$(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
ui_DATA = evolution-composer.ui
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index 047bf735bd..f917a2c70e 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -134,20 +134,24 @@ static void
action_pgp_encrypt_cb (GtkToggleAction *action,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static void
action_pgp_sign_cb (GtkToggleAction *action,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static void
@@ -198,12 +202,14 @@ static void
action_save_cb (GtkAction *action,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor = GTKHTML_EDITOR (composer);
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
const gchar *filename;
gint fd;
GError *error = NULL;
- filename = gtkhtml_editor_get_filename (editor);
+ editor = e_msg_composer_get_editor (composer);
+ filename = e_html_editor_get_filename (editor);
if (filename == NULL) {
gtk_action_activate (ACTION (SAVE_AS));
return;
@@ -233,7 +239,7 @@ action_save_cb (GtkAction *action,
} else
close (fd);
- if (!gtkhtml_editor_save (editor, filename, TRUE, &error)) {
+ if (!e_html_editor_save (editor, filename, TRUE, &error)) {
e_alert_submit (
E_ALERT_SINK (composer),
E_ALERT_NO_SAVE_FILE,
@@ -242,13 +248,15 @@ action_save_cb (GtkAction *action,
return;
}
- gtkhtml_editor_run_command (GTKHTML_EDITOR (composer), "saved");
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static void
action_save_as_cb (GtkAction *action,
EMsgComposer *composer)
{
+ EHTMLEditor *editor;
GtkWidget *dialog;
gchar *filename;
gint response;
@@ -272,8 +280,9 @@ action_save_as_cb (GtkAction *action,
if (response != GTK_RESPONSE_OK)
goto exit;
+ editor = e_msg_composer_get_editor (composer);
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
- gtkhtml_editor_set_filename (GTKHTML_EDITOR (composer), filename);
+ e_html_editor_set_filename (editor, filename);
g_free (filename);
gtk_action_activate (ACTION (SAVE));
@@ -300,20 +309,24 @@ static void
action_smime_encrypt_cb (GtkToggleAction *action,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static void
action_smime_sign_cb (GtkToggleAction *action,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static gboolean
@@ -514,15 +527,15 @@ e_composer_actions_init (EMsgComposer *composer)
GtkActionGroup *action_group;
GtkAccelGroup *accel_group;
GtkUIManager *ui_manager;
- GtkhtmlEditor *editor;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
gboolean visible;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
- web_view = e_msg_composer_get_web_view (composer);
- ui_manager = gtkhtml_editor_get_ui_manager (editor);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ ui_manager = e_html_editor_get_ui_manager (editor);
/* Composer Actions */
action_group = composer->priv->composer_actions;
@@ -566,23 +579,33 @@ e_composer_actions_init (EMsgComposer *composer)
ACTION (SAVE_DRAFT), "short-label", _("Save Draft"), NULL);
g_object_bind_property (
- composer, "html-mode",
+ view, "html-mode",
ACTION (PICTURE_GALLERY), "sensitive",
G_BINDING_SYNC_CREATE);
g_object_bind_property (
- web_view, "editable",
- GTKHTML_EDITOR_ACTION_EDIT_MENU (editor), "sensitive",
+ view, "editable",
+ e_html_editor_get_action (editor, "edit-menu"), "sensitive",
G_BINDING_SYNC_CREATE);
g_object_bind_property (
- web_view, "editable",
- GTKHTML_EDITOR_ACTION_FORMAT_MENU (editor), "sensitive",
+ view, "editable",
+ e_html_editor_get_action (editor, "format-menu"), "sensitive",
G_BINDING_SYNC_CREATE);
g_object_bind_property (
- web_view, "editable",
- GTKHTML_EDITOR_ACTION_INSERT_MENU (editor), "sensitive",
+ view, "editable",
+ e_html_editor_get_action (editor, "insert-menu"), "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ view, "editable",
+ e_html_editor_get_action (editor, "options-menu"), "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ view, "editable",
+ e_html_editor_get_action (editor, "picture-gallery"), "sensitive",
G_BINDING_SYNC_CREATE);
#if defined (HAVE_NSS)
diff --git a/composer/e-composer-actions.h b/composer/e-composer-actions.h
index 611154e9be..291953109e 100644
--- a/composer/e-composer-actions.h
+++ b/composer/e-composer-actions.h
@@ -18,7 +18,9 @@
#define E_COMPOSER_ACTIONS_H
#define E_COMPOSER_ACTION(composer, name) \
- (gtkhtml_editor_get_action (GTKHTML_EDITOR (composer), (name)))
+ (e_html_editor_get_action ( \
+ e_msg_composer_get_editor ( \
+ E_MSG_COMPOSER (composer)), (name)))
#define E_COMPOSER_ACTION_ATTACH(composer) \
E_COMPOSER_ACTION ((composer), "attach")
diff --git a/composer/e-composer-activity.c b/composer/e-composer-activity.c
deleted file mode 100644
index 513915f189..0000000000
--- a/composer/e-composer-activity.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * e-composer-activity.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "e-composer-private.h"
-
-#define E_COMPOSER_ACTIVITY_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityPrivate))
-
-struct _EComposerActivityPrivate {
- EMsgComposer *composer;
- gboolean saved_editable;
-};
-
-enum {
- PROP_0,
- PROP_COMPOSER
-};
-
-G_DEFINE_TYPE (
- EComposerActivity,
- e_composer_activity,
- E_TYPE_ACTIVITY)
-
-static void
-composer_activity_lock_interface (EComposerActivity *activity)
-{
- GtkActionGroup *action_group;
- EMsgComposer *composer;
- EWebViewGtkHTML *web_view;
- gboolean editable;
-
- composer = e_composer_activity_get_composer (activity);
-
- web_view = e_msg_composer_get_web_view (composer);
- editable = e_web_view_gtkhtml_get_editable (web_view);
- e_web_view_gtkhtml_set_editable (web_view, FALSE);
- activity->priv->saved_editable = editable;
-
- action_group = composer->priv->async_actions;
- gtk_action_group_set_sensitive (action_group, FALSE);
-}
-
-static void
-composer_activity_unlock_interface (EComposerActivity *activity)
-{
- GtkActionGroup *action_group;
- EMsgComposer *composer;
- EWebViewGtkHTML *web_view;
- gboolean editable;
-
- composer = e_composer_activity_get_composer (activity);
-
- editable = activity->priv->saved_editable;
- web_view = e_msg_composer_get_web_view (composer);
- e_web_view_gtkhtml_set_editable (web_view, editable);
-
- action_group = composer->priv->async_actions;
- gtk_action_group_set_sensitive (action_group, TRUE);
-}
-
-static void
-composer_activity_set_composer (EComposerActivity *activity,
- EMsgComposer *composer)
-{
- g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- g_return_if_fail (activity->priv->composer == NULL);
-
- activity->priv->composer = g_object_ref (composer);
-
- composer_activity_lock_interface (activity);
-}
-
-static void
-composer_activity_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_COMPOSER:
- composer_activity_set_composer (
- E_COMPOSER_ACTIVITY (object),
- g_value_get_object (value));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-composer_activity_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_COMPOSER:
- g_value_set_object (
- value, e_composer_activity_get_composer (
- E_COMPOSER_ACTIVITY (object)));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-composer_activity_dispose (GObject *object)
-{
- EComposerActivity *activity;
-
- activity = E_COMPOSER_ACTIVITY (object);
-
- if (activity->priv->composer != NULL) {
- composer_activity_unlock_interface (activity);
- g_object_unref (activity->priv->composer);
- activity->priv->composer = NULL;
- }
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_composer_activity_parent_class)->dispose (object);
-}
-
-static void
-e_composer_activity_class_init (EComposerActivityClass *class)
-{
- GObjectClass *object_class;
-
- g_type_class_add_private (class, sizeof (EComposerActivityPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->set_property = composer_activity_set_property;
- object_class->get_property = composer_activity_get_property;
- object_class->dispose = composer_activity_dispose;
-
- g_object_class_install_property (
- object_class,
- PROP_COMPOSER,
- g_param_spec_object (
- "composer",
- NULL,
- NULL,
- E_TYPE_MSG_COMPOSER,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void
-e_composer_activity_init (EComposerActivity *activity)
-{
- activity->priv = E_COMPOSER_ACTIVITY_GET_PRIVATE (activity);
-}
-
-EActivity *
-e_composer_activity_new (EMsgComposer *composer)
-{
- return g_object_new (
- E_TYPE_COMPOSER_ACTIVITY,
- "composer", composer, NULL);
-}
-
-EMsgComposer *
-e_composer_activity_get_composer (EComposerActivity *activity)
-{
- g_return_val_if_fail (E_IS_COMPOSER_ACTIVITY (activity), NULL);
-
- return activity->priv->composer;
-}
diff --git a/composer/e-composer-activity.h b/composer/e-composer-activity.h
deleted file mode 100644
index 0966eba332..0000000000
--- a/composer/e-composer-activity.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * e-composer-activity.h
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef E_COMPOSER_ACTIVITY_H
-#define E_COMPOSER_ACTIVITY_H
-
-#include <composer/e-msg-composer.h>
-
-/* Standard GObject macros */
-#define E_TYPE_COMPOSER_ACTIVITY \
- (e_composer_activity_get_type ())
-#define E_COMPOSER_ACTIVITY(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivity))
-#define E_COMPOSER_ACTIVITY_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
-#define E_IS_COMPOSER_ACTIVITY(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_COMPOSER_ACTIVITY))
-#define E_IS_COMPOSER_ACTIVITY_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_COMPOSER_ACTIVITY))
-#define E_COMPOSER_ACTIVITY_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EComposerActivity EComposerActivity;
-typedef struct _EComposerActivityClass EComposerActivityClass;
-typedef struct _EComposerActivityPrivate EComposerActivityPrivate;
-
-struct _EComposerActivity {
- EActivity parent;
- EComposerActivityPrivate *priv;
-};
-
-struct _EComposerActivityClass {
- EActivityClass parent_class;
-};
-
-GType e_composer_activity_get_type (void);
-EActivity * e_composer_activity_new (EMsgComposer *composer);
-EMsgComposer * e_composer_activity_get_composer
- (EComposerActivity *activity);
-
-G_END_DECLS
-
-#endif /* E_COMPOSER_ACTIVITY_H */
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 59e1625639..bc3b6d8078 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -27,15 +27,19 @@
/* Initial height of the picture gallery. */
#define GALLERY_INITIAL_HEIGHT 150
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+
static void
composer_setup_charset_menu (EMsgComposer *composer)
{
+ EHTMLEditor *editor;
GtkUIManager *ui_manager;
const gchar *path;
GList *list;
guint merge_id;
- ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer));
+ editor = e_msg_composer_get_editor (composer);
+ ui_manager = e_html_editor_get_ui_manager (editor);
path = "/main-menu/options-menu/charset-menu";
merge_id = gtk_ui_manager_new_merge_id (ui_manager);
@@ -58,62 +62,22 @@ composer_setup_charset_menu (EMsgComposer *composer)
}
static void
-msg_composer_url_requested_cb (GtkHTML *html,
- const gchar *uri,
- GtkHTMLStream *stream,
- EMsgComposer *composer)
-{
- GByteArray *array;
- GHashTable *hash_table;
- CamelDataWrapper *wrapper;
- CamelStream *camel_stream;
- CamelMimePart *mime_part;
-
- hash_table = composer->priv->inline_images_by_url;
- mime_part = g_hash_table_lookup (hash_table, uri);
-
- if (mime_part == NULL) {
- hash_table = composer->priv->inline_images;
- mime_part = g_hash_table_lookup (hash_table, uri);
- }
-
- /* If this is not an inline image request,
- * allow the signal emission to continue. */
- if (mime_part == NULL)
- return;
-
- array = g_byte_array_new ();
- camel_stream = camel_stream_mem_new_with_byte_array (array);
- wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
- camel_data_wrapper_decode_to_stream_sync (
- wrapper, camel_stream, NULL, NULL);
-
- gtk_html_write (html, stream, (gchar *) array->data, array->len);
-
- gtk_html_end (html, stream, GTK_HTML_STREAM_OK);
-
- g_object_unref (camel_stream);
-
- /* gtk_html_end() destroys the GtkHTMLStream, so we need to
- * stop the signal emission so nothing else tries to use it. */
- g_signal_stop_emission_by_name (html, "url-requested");
-}
-
-static void
composer_update_gallery_visibility (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GtkToggleAction *toggle_action;
gboolean gallery_active;
- gboolean html_mode;
+ gboolean is_html;
- editor = GTKHTML_EDITOR (composer);
- html_mode = gtkhtml_editor_get_html_mode (editor);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ is_html = e_html_editor_view_get_html_mode (view);
toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
gallery_active = gtk_toggle_action_get_active (toggle_action);
- if (html_mode && gallery_active) {
+ if (is_html && gallery_active) {
gtk_widget_show (composer->priv->gallery_scrolled_window);
gtk_widget_show (composer->priv->gallery_icon_view);
} else {
@@ -122,30 +86,16 @@ composer_update_gallery_visibility (EMsgComposer *composer)
}
}
-static void
-composer_spell_languages_changed (EMsgComposer *composer,
- GList *languages)
-{
- EComposerHeader *header;
- EComposerHeaderTable *table;
-
- table = e_msg_composer_get_header_table (composer);
- header = e_composer_header_table_get_header (
- table, E_COMPOSER_HEADER_SUBJECT);
-
- e_composer_spell_header_set_languages (
- E_COMPOSER_SPELL_HEADER (header), languages);
-}
-
void
e_composer_private_constructed (EMsgComposer *composer)
{
EMsgComposerPrivate *priv = composer->priv;
EFocusTracker *focus_tracker;
+ EComposerHeader *header;
EShell *shell;
- EWebViewGtkHTML *web_view;
EClientCache *client_cache;
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GtkUIManager *ui_manager;
GtkAction *action;
GtkWidget *container;
@@ -158,14 +108,14 @@ e_composer_private_constructed (EMsgComposer *composer)
gint ii;
GError *error = NULL;
- editor = GTKHTML_EDITOR (composer);
- ui_manager = gtkhtml_editor_get_ui_manager (editor);
+ editor = e_msg_composer_get_editor (composer);
+ ui_manager = e_html_editor_get_ui_manager (editor);
+ view = e_html_editor_get_view (editor);
settings = g_settings_new ("org.gnome.evolution.mail");
shell = e_msg_composer_get_shell (composer);
client_cache = e_shell_get_client_cache (shell);
- web_view = e_msg_composer_get_web_view (composer);
/* Each composer window gets its own window group. */
window = GTK_WINDOW (composer);
@@ -179,19 +129,17 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->extra_hdr_names = g_ptr_array_new ();
priv->extra_hdr_values = g_ptr_array_new ();
- priv->inline_images = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) NULL);
-
- priv->inline_images_by_url = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_object_unref);
-
priv->charset = e_composer_get_default_charset ();
+ priv->is_from_draft = FALSE;
priv->is_from_message = FALSE;
+ priv->is_from_new_message = FALSE;
+ priv->set_signature_from_message = FALSE;
+ priv->disable_signature = FALSE;
+ priv->busy = FALSE;
+ priv->saved_editable= FALSE;
+
+ priv->focused_entry = NULL;
e_composer_actions_init (composer);
@@ -216,48 +164,58 @@ e_composer_private_constructed (EMsgComposer *composer)
focus_tracker = e_focus_tracker_new (GTK_WINDOW (composer));
- action = gtkhtml_editor_get_action (editor, "cut");
+ action = e_html_editor_get_action (editor, "cut");
e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
- action = gtkhtml_editor_get_action (editor, "copy");
+ action = e_html_editor_get_action (editor, "copy");
e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
- action = gtkhtml_editor_get_action (editor, "paste");
+ action = e_html_editor_get_action (editor, "paste");
e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
- action = gtkhtml_editor_get_action (editor, "select-all");
+ action = e_html_editor_get_action (editor, "select-all");
e_focus_tracker_set_select_all_action (focus_tracker, action);
priv->focus_tracker = focus_tracker;
- container = editor->vbox;
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (composer), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
- /* Construct the activity bar. */
+ /* Construct the main menu and toolbar. */
- widget = e_activity_bar_new ();
+ widget = e_html_editor_get_managed_widget (editor, "/main-menu");
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- priv->activity_bar = g_object_ref_sink (widget);
- /* EActivityBar controls its own visibility. */
-
- /* Construct the alert bar for errors. */
+ gtk_widget_show (widget);
- widget = e_alert_bar_new ();
+ widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- priv->alert_bar = g_object_ref_sink (widget);
- /* EAlertBar controls its own visibility. */
+ gtk_widget_show (widget);
/* Construct the header table. */
widget = e_composer_header_table_new (client_cache);
gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- gtk_box_reorder_child (GTK_BOX (container), widget, 2);
- priv->header_table = g_object_ref_sink (widget);
+ priv->header_table = g_object_ref (widget);
gtk_widget_show (widget);
- g_signal_connect (
- G_OBJECT (composer), "spell-languages-changed",
- G_CALLBACK (composer_spell_languages_changed), NULL);
+ header = e_composer_header_table_get_header (
+ E_COMPOSER_HEADER_TABLE (widget),
+ E_COMPOSER_HEADER_SUBJECT);
+ g_object_bind_property (
+ view, "spell-checker",
+ header->input_widget, "spell-checker",
+ G_BINDING_SYNC_CREATE);
+
+ /* Construct the editing toolbars. We'll have to reparent
+ * the embedded EHTMLEditorView a little further down. */
+
+ widget = GTK_WIDGET (editor);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
/* Construct the attachment paned. */
@@ -267,8 +225,8 @@ e_composer_private_constructed (EMsgComposer *composer)
gtk_widget_show (widget);
g_object_bind_property (
- web_view, "editable",
- widget, "editable",
+ view, "editable",
+ widget, "sensitive",
G_BINDING_SYNC_CREATE);
container = e_attachment_paned_get_content_area (
@@ -288,13 +246,13 @@ e_composer_private_constructed (EMsgComposer *composer)
GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
gtk_widget_set_size_request (widget, -1, GALLERY_INITIAL_HEIGHT);
gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, FALSE);
- priv->gallery_scrolled_window = g_object_ref_sink (widget);
+ priv->gallery_scrolled_window = g_object_ref (widget);
gtk_widget_show (widget);
- /* Reparent the scrolled window containing the GtkHTML widget
- * into the content area of the top attachment pane. */
+ /* Reparent the scrolled window containing the web view
+ * widget into the content area of the top attachment pane. */
- widget = GTK_WIDGET (web_view);
+ widget = GTK_WIDGET (view);
widget = gtk_widget_get_parent (widget);
gtk_widget_reparent (widget, container);
@@ -310,16 +268,16 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->gallery_icon_view = g_object_ref_sink (widget);
g_free (gallery_path);
- e_signal_connect_notify (
- composer, "notify::html-mode",
- G_CALLBACK (composer_update_gallery_visibility), NULL);
+ e_signal_connect_notify_swapped (
+ view, "notify::mode",
+ G_CALLBACK (composer_update_gallery_visibility), composer);
g_signal_connect_swapped (
ACTION (PICTURE_GALLERY), "toggled",
G_CALLBACK (composer_update_gallery_visibility), composer);
- /* XXX What is this for? */
- g_object_set_data (G_OBJECT (composer), "vbox", editor->vbox);
+ /* Initial sync */
+ composer_update_gallery_visibility (composer);
/* Bind headers to their corresponding actions. */
@@ -361,20 +319,21 @@ e_composer_private_constructed (EMsgComposer *composer)
G_BINDING_SYNC_CREATE);
}
- /* Install a handler for inline images. */
+ /* Disable actions that start asynchronous activities while an
+ * asynchronous activity is in progress. We enforce this with
+ * a simple inverted binding to EMsgComposer's "busy" property. */
- /* XXX We no longer use GtkhtmlEditor::uri-requested because it
- * conflicts with EWebView's url_requested() method, which
- * unconditionally launches an async operation. I changed
- * GtkHTML::url-requested to be a G_SIGNAL_RUN_LAST so that
- * our handler runs first. If we can handle the request
- * we'll stop the signal emission to prevent EWebView from
- * launching an async operation. Messy, but works until we
- * switch to WebKit. --mbarnes */
+ g_object_bind_property (
+ composer, "busy",
+ priv->async_actions, "sensitive",
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN);
- g_signal_connect (
- web_view, "url-requested",
- G_CALLBACK (msg_composer_url_requested_cb), composer);
+ g_object_bind_property (
+ composer, "busy",
+ priv->header_table, "sensitive",
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN);
g_object_unref (settings);
}
@@ -389,21 +348,16 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->shell = NULL;
}
+ if (composer->priv->editor != NULL) {
+ g_object_unref (composer->priv->editor);
+ composer->priv->editor = NULL;
+ }
+
if (composer->priv->header_table != NULL) {
g_object_unref (composer->priv->header_table);
composer->priv->header_table = NULL;
}
- if (composer->priv->activity_bar != NULL) {
- g_object_unref (composer->priv->activity_bar);
- composer->priv->activity_bar = NULL;
- }
-
- if (composer->priv->alert_bar != NULL) {
- g_object_unref (composer->priv->alert_bar);
- composer->priv->alert_bar = NULL;
- }
-
if (composer->priv->attachment_paned != NULL) {
g_object_unref (composer->priv->attachment_paned);
composer->priv->attachment_paned = NULL;
@@ -434,11 +388,10 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->composer_actions = NULL;
}
- g_clear_object (&composer->priv->gallery_icon_view);
- g_clear_object (&composer->priv->gallery_scrolled_window);
-
- g_hash_table_remove_all (composer->priv->inline_images);
- g_hash_table_remove_all (composer->priv->inline_images_by_url);
+ if (composer->priv->gallery_scrolled_window != NULL) {
+ g_object_unref (composer->priv->gallery_scrolled_window);
+ composer->priv->gallery_scrolled_window = NULL;
+ }
if (composer->priv->redirect != NULL) {
g_object_unref (composer->priv->redirect);
@@ -462,10 +415,6 @@ e_composer_private_finalize (EMsgComposer *composer)
g_free (composer->priv->charset);
g_free (composer->priv->mime_type);
g_free (composer->priv->mime_body);
- g_free (composer->priv->selected_signature_uid);
-
- g_hash_table_destroy (composer->priv->inline_images);
- g_hash_table_destroy (composer->priv->inline_images_by_url);
}
gchar *
@@ -525,92 +474,13 @@ e_composer_get_default_charset (void)
return charset;
}
-gchar *
-e_composer_decode_clue_value (const gchar *encoded_value)
-{
- GString *buffer;
- const gchar *cp;
-
- /* Decode a GtkHtml "ClueFlow" value. */
-
- g_return_val_if_fail (encoded_value != NULL, NULL);
-
- buffer = g_string_sized_new (strlen (encoded_value));
-
- /* Copy the value, decoding escaped characters as we go. */
- cp = encoded_value;
- while (*cp != '\0') {
- if (*cp == '.') {
- cp++;
- switch (*cp) {
- case '.':
- g_string_append_c (buffer, '.');
- break;
- case '1':
- g_string_append_c (buffer, '"');
- break;
- case '2':
- g_string_append_c (buffer, '=');
- break;
- default:
- /* Invalid escape sequence. */
- g_string_free (buffer, TRUE);
- return NULL;
- }
- } else
- g_string_append_c (buffer, *cp);
- cp++;
- }
-
- return g_string_free (buffer, FALSE);
-}
-
-gchar *
-e_composer_encode_clue_value (const gchar *decoded_value)
-{
- gchar *encoded_value;
- gchar **strv;
-
- /* Encode a GtkHtml "ClueFlow" value. */
-
- g_return_val_if_fail (decoded_value != NULL, NULL);
-
- /* XXX This is inefficient but easy to understand. */
-
- encoded_value = g_strdup (decoded_value);
-
- /* Substitution: '.' --> '..' (do this first) */
- if (strchr (encoded_value, '.') != NULL) {
- strv = g_strsplit (encoded_value, ".", 0);
- g_free (encoded_value);
- encoded_value = g_strjoinv ("..", strv);
- g_strfreev (strv);
- }
-
- /* Substitution: '"' --> '.1' */
- if (strchr (encoded_value, '"') != NULL) {
- strv = g_strsplit (encoded_value, """", 0);
- g_free (encoded_value);
- encoded_value = g_strjoinv (".1", strv);
- g_strfreev (strv);
- }
-
- /* Substitution: '=' --> '.2' */
- if (strchr (encoded_value, '=') != NULL) {
- strv = g_strsplit (encoded_value, "=", 0);
- g_free (encoded_value);
- encoded_value = g_strjoinv (".2", strv);
- g_strfreev (strv);
- }
-
- return encoded_value;
-}
-
gboolean
e_composer_paste_html (EMsgComposer *composer,
GtkClipboard *clipboard)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *editor_selection;
gchar *html;
g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
@@ -619,9 +489,15 @@ e_composer_paste_html (EMsgComposer *composer,
html = e_clipboard_wait_for_html (clipboard);
g_return_val_if_fail (html != NULL, FALSE);
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_insert_html (editor, html);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+ e_html_editor_selection_insert_html (editor_selection, html);
+ e_html_editor_view_check_magic_links (view, FALSE);
+ e_html_editor_view_force_spell_check (view);
+
+ e_html_editor_selection_scroll_to_caret (editor_selection);
g_free (html);
return TRUE;
@@ -631,7 +507,8 @@ gboolean
e_composer_paste_image (EMsgComposer *composer,
GtkClipboard *clipboard)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *html_editor_view;
EAttachmentStore *store;
EAttachmentView *view;
GdkPixbuf *pixbuf = NULL;
@@ -643,7 +520,6 @@ e_composer_paste_image (EMsgComposer *composer,
g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE);
- editor = GTKHTML_EDITOR (composer);
view = e_msg_composer_get_attachment_view (composer);
store = e_attachment_view_get_store (view);
@@ -673,9 +549,15 @@ e_composer_paste_image (EMsgComposer *composer,
/* In HTML mode, paste the image into the message body.
* In text mode, add the image to the attachment store. */
- if (gtkhtml_editor_get_html_mode (editor))
- gtkhtml_editor_insert_image (editor, uri);
- else {
+ editor = e_msg_composer_get_editor (composer);
+ html_editor_view = e_html_editor_get_view (editor);
+ if (e_html_editor_view_get_html_mode (html_editor_view)) {
+ EHTMLEditorSelection *selection;
+
+ selection = e_html_editor_view_get_selection (html_editor_view);
+ e_html_editor_selection_insert_image (selection, uri);
+ e_html_editor_selection_scroll_to_caret (selection);
+ } else {
EAttachment *attachment;
attachment = e_attachment_new_for_uri (uri);
@@ -705,7 +587,9 @@ gboolean
e_composer_paste_text (EMsgComposer *composer,
GtkClipboard *clipboard)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *editor_selection;
gchar *text;
g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
@@ -714,8 +598,18 @@ e_composer_paste_text (EMsgComposer *composer,
text = gtk_clipboard_wait_for_text (clipboard);
g_return_val_if_fail (text != NULL, FALSE);
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_insert_text (editor, text);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+ /* If WebView doesn't have focus, focus it */
+ if (!gtk_widget_has_focus (GTK_WIDGET (view)))
+ gtk_widget_grab_focus (GTK_WIDGET (view));
+
+ e_html_editor_selection_insert_text (editor_selection, text);
+
+ e_html_editor_view_check_magic_links (view, FALSE);
+ e_html_editor_view_force_spell_check (view);
+ e_html_editor_selection_scroll_to_caret (editor_selection);
g_free (text);
@@ -757,6 +651,35 @@ e_composer_paste_uris (EMsgComposer *composer,
}
gboolean
+e_composer_selection_is_base64_uris (EMsgComposer *composer,
+ GtkSelectionData *selection)
+{
+ gboolean all_base64_uris = TRUE;
+ gchar **uris;
+ guint ii;
+
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+ g_return_val_if_fail (selection != NULL, FALSE);
+
+ uris = gtk_selection_data_get_uris (selection);
+
+ if (!uris)
+ return FALSE;
+
+ for (ii = 0; uris[ii] != NULL; ii++) {
+ if (!((g_str_has_prefix (uris[ii], "data:") || strstr (uris[ii], ";data:"))
+ && strstr (uris[ii], ";base64,"))) {
+ all_base64_uris = FALSE;
+ break;
+ }
+ }
+
+ g_strfreev (uris);
+
+ return all_base64_uris;
+}
+
+gboolean
e_composer_selection_is_image_uris (EMsgComposer *composer,
GtkSelectionData *selection)
{
@@ -769,7 +692,7 @@ e_composer_selection_is_image_uris (EMsgComposer *composer,
uris = gtk_selection_data_get_uris (selection);
- if (uris == NULL)
+ if (!uris)
return FALSE;
for (ii = 0; uris[ii] != NULL; ii++) {
@@ -859,19 +782,255 @@ use_top_signature (EMsgComposer *composer)
}
static void
+composer_size_allocate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *scrolled_window;
+ GtkAdjustment *adj;
+
+ scrolled_window = gtk_widget_get_parent (GTK_WIDGET (widget));
+ adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window));
+
+ /* Scroll only when there is some size allocated */
+ if (gtk_adjustment_get_upper (adj) != 0.0) {
+ /* Scroll web view down to caret */
+ gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
+ gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window), adj);
+ /* Disconnect because we don't want to scroll down the view on every window size change */
+ g_signal_handlers_disconnect_by_func (
+ widget, G_CALLBACK (composer_size_allocate_cb), NULL);
+ }
+}
+
+static void
+insert_paragraph_with_input (WebKitDOMElement *paragraph,
+ WebKitDOMElement *body)
+{
+ WebKitDOMNode *node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+ if (node) {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (paragraph),
+ node,
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (paragraph),
+ NULL);
+ }
+}
+
+static void
+composer_move_caret (EMsgComposer *composer)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *editor_selection;
+ GSettings *settings;
+ gboolean start_bottom, html_mode, top_signature;
+ gboolean has_paragraphs_in_body = TRUE;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMElement *input_start, *element, *signature;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMNodeList *list, *blockquotes;
+ WebKitDOMRange *new_range;
+
+ /* When there is an option composer-reply-start-bottom set we have
+ * to move the caret between reply and signature. */
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+ g_object_unref (settings);
+
+ top_signature =
+ use_top_signature (composer) &&
+ !composer->priv->is_from_message &&
+ !composer->priv->is_from_new_message;
+
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+ html_mode = e_html_editor_view_get_html_mode (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+
+ body = webkit_dom_document_get_body (document);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL);
+ new_range = webkit_dom_document_create_range (document);
+
+ element = webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position");
+ /* Caret position found => composer mode changed */
+ if (element) {
+ e_html_editor_selection_restore_caret_position (editor_selection);
+ /* We want to force spellcheck just in case that we switched to plain
+ * text mode (when switching to html mode, the underlined words are
+ * preserved */
+ if (!html_mode)
+ e_html_editor_view_force_spell_check (view);
+ return;
+ }
+
+ /* If editing message as new don't handle with caret */
+ if (composer->priv->is_from_message || composer->priv->is_from_draft) {
+ if (composer->priv->is_from_message)
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body),
+ "data-edit-as-new",
+ "",
+ NULL);
+ e_html_editor_selection_restore_caret_position (editor_selection);
+ e_html_editor_selection_scroll_to_caret (editor_selection);
+
+ e_html_editor_view_force_spell_check (view);
+ return;
+ }
+
+ e_html_editor_selection_block_selection_changed (editor_selection);
+
+ /* When the new message is written from the beginning - note it into body */
+ if (composer->priv->is_from_new_message) {
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-new-message", "", NULL);
+ }
+
+ list = webkit_dom_document_get_elements_by_class_name (document, "-x-evo-paragraph");
+ signature = webkit_dom_document_query_selector (document, ".-x-evo-signature", NULL);
+ /* Situation when wrapped paragraph is just in signature and not in message body */
+ if (webkit_dom_node_list_get_length (list) == 1) {
+ if (signature && webkit_dom_element_query_selector (signature, ".-x-evo-paragraph", NULL))
+ has_paragraphs_in_body = FALSE;
+ }
+
+ if (webkit_dom_node_list_get_length (list) == 0)
+ has_paragraphs_in_body = FALSE;
+
+ blockquotes = webkit_dom_document_get_elements_by_tag_name (document, "blockquote");
+
+ if (!has_paragraphs_in_body) {
+ element = e_html_editor_selection_get_paragraph_element (
+ editor_selection, document, -1, 0);
+ webkit_dom_element_set_id (element, "-x-evo-input-start");
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element), UNICODE_ZERO_WIDTH_SPACE, NULL);
+ if (top_signature)
+ element_add_class (element, "-x-evo-top-signature");
+ }
+
+ if (start_bottom) {
+ if (webkit_dom_node_list_get_length (blockquotes) != 0) {
+ if (!has_paragraphs_in_body) {
+ if (!top_signature) {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (element),
+ signature ?
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature)) :
+ webkit_dom_node_get_next_sibling (
+ webkit_dom_node_list_item (
+ blockquotes, 0)),
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ }
+
+ e_html_editor_selection_restore_caret_position (editor_selection);
+ if (!html_mode)
+ e_html_editor_view_quote_plain_text (view);
+ e_html_editor_view_force_spell_check (view);
+
+ input_start = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-input-start");
+ if (input_start)
+ webkit_dom_range_select_node_contents (
+ new_range, WEBKIT_DOM_NODE (input_start), NULL);
+
+ webkit_dom_range_collapse (new_range, FALSE, NULL);
+ } else {
+ if (!has_paragraphs_in_body)
+ insert_paragraph_with_input (
+ element, WEBKIT_DOM_ELEMENT (body));
+
+ webkit_dom_range_select_node_contents (
+ new_range,
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (body)),
+ NULL);
+ webkit_dom_range_collapse (new_range, TRUE, NULL);
+ }
+
+ g_signal_connect (
+ view, "size-allocate",
+ G_CALLBACK (composer_size_allocate_cb), NULL);
+ } else {
+ /* Move caret on the beginning of message */
+ if (!has_paragraphs_in_body) {
+ insert_paragraph_with_input (
+ element, WEBKIT_DOM_ELEMENT (body));
+
+ if (webkit_dom_node_list_get_length (blockquotes) != 0) {
+ if (!html_mode) {
+ WebKitDOMNode *blockquote;
+
+ blockquote = webkit_dom_node_list_item (blockquotes, 0);
+
+ /* FIXME determine when we can skip this */
+ e_html_editor_selection_wrap_paragraph (
+ editor_selection,
+ WEBKIT_DOM_ELEMENT (blockquote));
+
+ e_html_editor_selection_restore_caret_position (editor_selection);
+ e_html_editor_view_quote_plain_text (view);
+ body = webkit_dom_document_get_body (document);
+ }
+ }
+ }
+
+ e_html_editor_view_force_spell_check (view);
+
+ webkit_dom_range_select_node_contents (
+ new_range,
+ WEBKIT_DOM_NODE (
+ webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))),
+ NULL);
+ webkit_dom_range_collapse (new_range, TRUE, NULL);
+ }
+
+ webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+ webkit_dom_dom_selection_add_range (dom_selection, new_range);
+
+ e_html_editor_selection_unblock_selection_changed (editor_selection);
+}
+
+static void
composer_load_signature_cb (EMailSignatureComboBox *combo_box,
GAsyncResult *result,
EMsgComposer *composer)
{
GString *html_buffer = NULL;
- GtkhtmlEditor *editor;
gchar *contents = NULL;
gsize length = 0;
const gchar *active_id;
- gchar *encoded_uid = NULL;
gboolean top_signature;
gboolean is_html;
GError *error = NULL;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *signatures;
+ gulong list_length, ii;
+ GSettings *settings;
+ gboolean start_bottom;
e_mail_signature_combo_box_load_selected_finish (
combo_box, result, &contents, &length, &is_html, &error);
@@ -887,7 +1046,12 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
* Always put the signature at the bottom for that case. */
top_signature =
use_top_signature (composer) &&
- !composer->priv->is_from_message;
+ !composer->priv->is_from_message &&
+ !composer->priv->is_from_new_message;
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+ g_object_unref (settings);
if (contents == NULL)
goto insert;
@@ -911,24 +1075,13 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
/* The combo box active ID is the signature's ESource UID. */
active_id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box));
- if (active_id != NULL && *active_id != '\0')
- encoded_uid = e_composer_encode_clue_value (active_id);
-
g_string_append_printf (
html_buffer,
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- " key=\"signature\" value=\"1\">-->"
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- " key=\"signature_name\" value=\"uid:%s\">-->",
- (encoded_uid != NULL) ? encoded_uid : "");
-
- g_string_append (
- html_buffer,
- "<TABLE WIDTH=\"100%%\" CELLSPACING=\"0\""
- " CELLPADDING=\"0\"><TR><TD>");
+ "<SPAN class=\"-x-evo-signature\" id=\"1\" name=\"%s\">",
+ (active_id != NULL) ? active_id : "");
if (!is_html)
- g_string_append (html_buffer, "<PRE>\n");
+ g_string_append (html_buffer, "<PRE>");
/* The signature dash convention ("-- \n") is specified
* in the "Son of RFC 1036", section 4.3.2.
@@ -939,8 +1092,8 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
const gchar *delim_nl;
if (is_html) {
- delim = "-- \n<BR>";
- delim_nl = "\n-- \n<BR>";
+ delim = "-- <BR>";
+ delim_nl = "\n-- <BR>";
} else {
delim = "-- \n";
delim_nl = "\n-- \n";
@@ -958,73 +1111,148 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
g_string_append_len (html_buffer, contents, length);
if (!is_html)
- g_string_append (html_buffer, "</PRE>\n");
-
- if (top_signature)
- g_string_append (html_buffer, "<BR>");
-
- g_string_append (html_buffer, "</TD></TR></TABLE>");
+ g_string_append (html_buffer, "</PRE>");
- g_free (encoded_uid);
+ g_string_append (html_buffer, "</SPAN>");
g_free (contents);
insert:
/* Remove the old signature and insert the new one. */
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ signatures = webkit_dom_document_get_elements_by_class_name (
+ document, "-x-evo-signature");
+ list_length = webkit_dom_node_list_get_length (signatures);
+ for (ii = 0; ii < list_length; ii++) {
+ WebKitDOMNode *node;
+ gchar *id;
+
+ node = webkit_dom_node_list_item (signatures, ii);
+ id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node));
+
+ /* When we are editing a message with signature we need to set active
+ * signature id in signature combo box otherwise no signature will be
+ * added but we have to do it just once when the composer opens */
+ if (composer->priv->is_from_message && composer->priv->set_signature_from_message) {
+ gchar *name = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "name");
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), name);
+ g_free (name);
+ composer->priv->set_signature_from_message = FALSE;
+ }
+
+ if (id && (strlen (id) == 1) && (*id == '1')) {
+ /* We have to remove the div containing the span with signature */
+ WebKitDOMNode *next_sibling;
+ WebKitDOMNode *parent;
+
+ parent = webkit_dom_node_get_parent_node (node);
+ next_sibling = webkit_dom_node_get_next_sibling (parent);
- /* This prevents our command before/after callbacks from
- * screwing around with the signature as we insert it. */
- composer->priv->in_signature_insert = TRUE;
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling))
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (next_sibling),
+ next_sibling,
+ NULL);
- gtkhtml_editor_freeze (editor);
- gtkhtml_editor_run_command (editor, "cursor-position-save");
- gtkhtml_editor_undo_begin (editor, "Set signature", "Reset signature");
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (parent),
+ parent,
+ NULL);
+
+ g_free (id);
+ break;
+ }
- gtkhtml_editor_run_command (editor, "block-selection");
- gtkhtml_editor_run_command (editor, "cursor-bod");
- if (gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1")) {
- gtkhtml_editor_run_command (editor, "select-paragraph");
- gtkhtml_editor_run_command (editor, "delete");
- gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
- gtkhtml_editor_run_command (editor, "delete-back");
+ g_free (id);
}
- gtkhtml_editor_run_command (editor, "unblock-selection");
if (html_buffer != NULL) {
- gtkhtml_editor_run_command (editor, "insert-paragraph");
- if (!gtkhtml_editor_run_command (editor, "cursor-backward"))
- gtkhtml_editor_run_command (editor, "insert-paragraph");
- else
- gtkhtml_editor_run_command (editor, "cursor-forward");
-
- gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
- gtkhtml_editor_run_command (editor, "indent-zero");
- gtkhtml_editor_run_command (editor, "style-normal");
- gtkhtml_editor_insert_html (editor, html_buffer->str);
+ if (*html_buffer->str) {
+ WebKitDOMElement *element;
+ WebKitDOMHTMLElement *body;
+
+ body = webkit_dom_document_get_body (document);
+ element = webkit_dom_document_create_element (document, "DIV", NULL);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element), html_buffer->str, NULL);
+
+ if (top_signature) {
+ WebKitDOMNode *signature_inserted;
+ WebKitDOMNode *child =
+ webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+ WebKitDOMElement *br =
+ webkit_dom_document_create_element (
+ document, "br", NULL);
+
+ if (start_bottom) {
+ signature_inserted = webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (element),
+ child,
+ NULL);
+ } else {
+ WebKitDOMElement *input_start =
+ webkit_dom_document_get_element_by_id (
+ document, "-x-evo-input-start");
+ /* When we are using signature on top the caret
+ * should be before the signature */
+ signature_inserted = webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (element),
+ input_start ?
+ webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (input_start)) :
+ child,
+ NULL);
+ }
+
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (br),
+ webkit_dom_node_get_next_sibling (signature_inserted),
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ }
g_string_free (html_buffer, TRUE);
-
- } else if (top_signature) {
- /* Insert paragraph after the signature ClueFlow stuff. */
- if (gtkhtml_editor_run_command (editor, "cursor-forward"))
- gtkhtml_editor_run_command (editor, "insert-paragraph");
}
- gtkhtml_editor_undo_end (editor);
- gtkhtml_editor_run_command (editor, "cursor-position-restore");
- gtkhtml_editor_thaw (editor);
-
- composer->priv->in_signature_insert = FALSE;
+ composer_move_caret (composer);
exit:
g_object_unref (composer);
}
-static gboolean
-is_null_or_none (const gchar *text)
+static void
+composer_web_view_load_status_changed_cb (WebKitWebView *webkit_web_view,
+ GParamSpec *pspec,
+ EMsgComposer *composer)
{
- return !text || g_strcmp0 (text, "none") == 0;
+ WebKitLoadStatus status;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+ status = webkit_web_view_get_load_status (webkit_web_view);
+
+ if (status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ g_signal_handlers_disconnect_by_func (
+ webkit_web_view,
+ G_CALLBACK (composer_web_view_load_status_changed_cb),
+ NULL);
+
+ e_composer_update_signature (composer);
}
void
@@ -1032,29 +1260,35 @@ e_composer_update_signature (EMsgComposer *composer)
{
EComposerHeaderTable *table;
EMailSignatureComboBox *combo_box;
- const gchar *signature_uid;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitLoadStatus status;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- /* Do nothing if we're redirecting a message. */
- if (composer->priv->redirect)
+ /* Do nothing if we're redirecting a message or we disabled the signature * on purpose */
+ if (composer->priv->redirect || composer->priv->disable_signature)
return;
table = e_msg_composer_get_header_table (composer);
- signature_uid = e_composer_header_table_get_signature_uid (table);
-
- /* this is a case when the signature combo cleared itself for a reload */
- if (!signature_uid)
- return;
-
- if (g_strcmp0 (signature_uid, composer->priv->selected_signature_uid) == 0 ||
- (is_null_or_none (signature_uid) && is_null_or_none (composer->priv->selected_signature_uid)))
- return;
-
- g_free (composer->priv->selected_signature_uid);
- composer->priv->selected_signature_uid = g_strdup (signature_uid);
-
combo_box = e_composer_header_table_get_signature_combo_box (table);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
+ status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
+ /* If document is not loaded, we will wait for him */
+ if (status != WEBKIT_LOAD_FINISHED) {
+ /* Disconnect previous handlers */
+ g_signal_handlers_disconnect_by_func (
+ WEBKIT_WEB_VIEW (view),
+ G_CALLBACK (composer_web_view_load_status_changed_cb),
+ composer);
+ g_signal_connect (
+ WEBKIT_WEB_VIEW(view), "notify::load-status",
+ G_CALLBACK (composer_web_view_load_status_changed_cb),
+ composer);
+ return;
+ }
/* XXX Signature files should be local and therefore load quickly,
* so while we do load them asynchronously we don't allow for
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index b09e025e0c..46c72b78ee 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -32,7 +32,6 @@
#include <libebackend/libebackend.h>
#include "e-composer-actions.h"
-#include "e-composer-activity.h"
#include "e-composer-header-table.h"
#ifdef HAVE_XFREE
@@ -58,11 +57,11 @@ struct _EMsgComposerPrivate {
gpointer shell; /* weak pointer */
+ EHTMLEditor *editor;
+
/*** UI Management ***/
GtkWidget *header_table;
- GtkWidget *activity_bar;
- GtkWidget *alert_bar;
GtkWidget *attachment_paned;
EFocusTracker *focus_tracker;
@@ -82,10 +81,6 @@ struct _EMsgComposerPrivate {
GtkWidget *address_dialog;
- GHashTable *inline_images;
- GHashTable *inline_images_by_url;
- GList *current_images;
-
gchar *mime_type;
gchar *mime_body;
gchar *charset;
@@ -97,9 +92,18 @@ struct _EMsgComposerPrivate {
CamelMimeMessage *redirect;
+ gboolean busy;
+ gboolean disable_signature;
+ gboolean is_from_draft;
gboolean is_from_message;
-
- gchar *selected_signature_uid;
+ gboolean is_from_new_message;
+ /* The web view is uneditable while the editor is busy.
+ * This is used to restore the previous editable state. */
+ gboolean saved_editable;
+ gboolean set_signature_from_message;
+
+ gint focused_entry_selection_start;
+ gint focused_entry_selection_end;
};
void e_composer_private_constructed (EMsgComposer *composer);
@@ -121,6 +125,9 @@ gboolean e_composer_paste_text (EMsgComposer *composer,
GtkClipboard *clipboard);
gboolean e_composer_paste_uris (EMsgComposer *composer,
GtkClipboard *clipboard);
+gboolean e_composer_selection_is_base64_uris
+ (EMsgComposer *composer,
+ GtkSelectionData *selection);
gboolean e_composer_selection_is_image_uris
(EMsgComposer *composer,
GtkSelectionData *selection);
diff --git a/composer/e-composer-spell-header.c b/composer/e-composer-spell-header.c
index c4fc471bc9..b2d2dfa2a4 100644
--- a/composer/e-composer-spell-header.c
+++ b/composer/e-composer-spell-header.c
@@ -63,16 +63,3 @@ e_composer_spell_header_new_button (ESourceRegistry *registry,
"registry", registry, NULL);
}
-void
-e_composer_spell_header_set_languages (EComposerSpellHeader *header,
- GList *languages)
-{
- ESpellEntry *spell_entry;
-
- g_return_if_fail (header != NULL);
-
- spell_entry = E_SPELL_ENTRY (E_COMPOSER_HEADER (header)->input_widget);
- g_return_if_fail (spell_entry != NULL);
-
- e_spell_entry_set_languages (spell_entry, languages);
-}
diff --git a/composer/e-composer-spell-header.h b/composer/e-composer-spell-header.h
index d440ee157c..f0034a0f84 100644
--- a/composer/e-composer-spell-header.h
+++ b/composer/e-composer-spell-header.h
@@ -66,9 +66,6 @@ EComposerHeader *
e_composer_spell_header_new_button
(ESourceRegistry *registry,
const gchar *label);
-void e_composer_spell_header_set_languages
- (EComposerSpellHeader *header,
- GList *languages);
G_END_DECLS
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 57b716c1ad..d3891beb4e 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -35,6 +35,7 @@
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
+#include <enchant/enchant.h>
#include "e-composer-private.h"
@@ -72,19 +73,22 @@ struct _AsyncContext {
/* Flags for building a message. */
typedef enum {
- COMPOSER_FLAG_HTML_CONTENT = 1 << 0,
- COMPOSER_FLAG_SAVE_OBJECT_DATA = 1 << 1,
- COMPOSER_FLAG_PRIORITIZE_MESSAGE = 1 << 2,
- COMPOSER_FLAG_REQUEST_READ_RECEIPT = 1 << 3,
- COMPOSER_FLAG_PGP_SIGN = 1 << 4,
- COMPOSER_FLAG_PGP_ENCRYPT = 1 << 5,
- COMPOSER_FLAG_SMIME_SIGN = 1 << 6,
- COMPOSER_FLAG_SMIME_ENCRYPT = 1 << 7,
- COMPOSER_FLAG_DRAFT = 1 << 8
+ COMPOSER_FLAG_HTML_CONTENT = 1 << 0,
+ COMPOSER_FLAG_SAVE_OBJECT_DATA = 1 << 1,
+ COMPOSER_FLAG_PRIORITIZE_MESSAGE = 1 << 2,
+ COMPOSER_FLAG_REQUEST_READ_RECEIPT = 1 << 3,
+ COMPOSER_FLAG_PGP_SIGN = 1 << 4,
+ COMPOSER_FLAG_PGP_ENCRYPT = 1 << 5,
+ COMPOSER_FLAG_SMIME_SIGN = 1 << 6,
+ COMPOSER_FLAG_SMIME_ENCRYPT = 1 << 7,
+ COMPOSER_FLAG_HTML_MODE = 1 << 8,
+ COMPOSER_FLAG_SAVE_DRAFT = 1 << 9
} ComposerFlags;
enum {
PROP_0,
+ PROP_BUSY,
+ PROP_EDITOR,
PROP_FOCUS_TRACKER,
PROP_SHELL
};
@@ -98,6 +102,24 @@ enum {
LAST_SIGNAL
};
+enum DndTargetType {
+ DND_TARGET_TYPE_TEXT_URI_LIST,
+ DND_TARGET_TYPE_MOZILLA_URL,
+ DND_TARGET_TYPE_TEXT_HTML,
+ DND_TARGET_TYPE_UTF8_STRING,
+ DND_TARGET_TYPE_TEXT_PLAIN,
+ DND_TARGET_TYPE_STRING
+};
+
+static GtkTargetEntry drag_dest_targets[] = {
+ { (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
+ { (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
+ { (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
+ { (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
+ { (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
+ { (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
+};
+
static guint signals[LAST_SIGNAL];
/* used by e_msg_composer_add_message_attachments () */
@@ -128,14 +150,10 @@ static void handle_multipart_signed (EMsgComposer *composer,
GCancellable *cancellable,
gint depth);
-static void e_msg_composer_alert_sink_init (EAlertSinkInterface *iface);
-
G_DEFINE_TYPE_WITH_CODE (
EMsgComposer,
e_msg_composer,
- GTKHTML_TYPE_EDITOR,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_ALERT_SINK, e_msg_composer_alert_sink_init)
+ GTK_TYPE_WINDOW,
G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
static void
@@ -407,45 +425,6 @@ best_charset (GByteArray *buf,
return g_strdup (charset);
}
-static void
-clear_current_images (EMsgComposer *composer)
-{
- EMsgComposerPrivate *p = composer->priv;
- g_list_free (p->current_images);
- p->current_images = NULL;
-}
-
-void
-e_msg_composer_clear_inlined_table (EMsgComposer *composer)
-{
- EMsgComposerPrivate *p = composer->priv;
-
- g_hash_table_remove_all (p->inline_images);
- g_hash_table_remove_all (p->inline_images_by_url);
-}
-
-static void
-add_inlined_images (EMsgComposer *composer,
- CamelMultipart *multipart)
-{
- EMsgComposerPrivate *p = composer->priv;
-
- GList *d = p->current_images;
- GHashTable *added;
-
- added = g_hash_table_new (g_direct_hash, g_direct_equal);
- while (d) {
- CamelMimePart *part = d->data;
-
- if (!g_hash_table_lookup (added, part)) {
- camel_multipart_add_part (multipart, part);
- g_hash_table_insert (added, part, part);
- }
- d = d->next;
- }
- g_hash_table_destroy (added);
-}
-
/* These functions builds a CamelMimeMessage for the message that the user has
* composed in 'composer'.
*/
@@ -1041,6 +1020,25 @@ composer_build_message_thread (GSimpleAsyncResult *simple,
}
static void
+composer_add_evolution_composer_mode_header (CamelMedium *medium,
+ ComposerFlags flags)
+{
+ GString *string;
+
+ string = g_string_sized_new (128);
+
+ if (flags & COMPOSER_FLAG_HTML_MODE)
+ g_string_append (string, "text/html");
+ else
+ g_string_append (string, "text/plain");
+
+ camel_medium_add_header (
+ medium, "X-Evolution-Composer-Mode", string->str);
+
+ g_string_free (string, TRUE);
+}
+
+static void
composer_add_evolution_format_header (CamelMedium *medium,
ComposerFlags flags)
{
@@ -1082,7 +1080,6 @@ composer_build_message (EMsgComposer *composer,
EMsgComposerPrivate *priv;
GSimpleAsyncResult *simple;
AsyncContext *context;
- GtkhtmlEditor *editor;
EAttachmentView *view;
EAttachmentStore *store;
EComposerHeaderTable *table;
@@ -1103,7 +1100,6 @@ composer_build_message (EMsgComposer *composer,
gint i;
priv = composer->priv;
- editor = GTKHTML_EDITOR (composer);
table = e_msg_composer_get_header_table (composer);
view = e_msg_composer_get_attachment_view (composer);
store = e_attachment_view_get_store (view);
@@ -1120,19 +1116,17 @@ composer_build_message (EMsgComposer *composer,
context->session = e_msg_composer_ref_session (composer);
context->from = e_msg_composer_get_from (composer);
- if ((flags & COMPOSER_FLAG_DRAFT) == 0) {
- if ((flags & COMPOSER_FLAG_PGP_SIGN) != 0)
- context->pgp_sign = TRUE;
+ if (flags & COMPOSER_FLAG_PGP_SIGN)
+ context->pgp_sign = TRUE;
- if ((flags & COMPOSER_FLAG_PGP_ENCRYPT) != 0)
- context->pgp_encrypt = TRUE;
+ if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+ context->pgp_encrypt = TRUE;
- if ((flags & COMPOSER_FLAG_SMIME_SIGN) != 0)
- context->smime_sign = TRUE;
+ if (flags & COMPOSER_FLAG_SMIME_SIGN)
+ context->smime_sign = TRUE;
- if ((flags & COMPOSER_FLAG_SMIME_ENCRYPT) != 0)
- context->smime_encrypt = TRUE;
- }
+ if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+ context->smime_encrypt = TRUE;
context->need_thread =
context->pgp_sign || context->pgp_encrypt ||
@@ -1210,6 +1204,41 @@ composer_build_message (EMsgComposer *composer,
composer_add_evolution_format_header (
CAMEL_MEDIUM (context->message), flags);
+ /* X-Evolution-Composer-Mode */
+ composer_add_evolution_composer_mode_header (
+ CAMEL_MEDIUM (context->message), flags);
+
+ if (flags & COMPOSER_FLAG_SAVE_DRAFT) {
+ gchar *text;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ data = g_byte_array_new ();
+
+ e_html_editor_view_embed_styles (view);
+ e_html_editor_selection_save_caret_position (selection);
+
+ text = e_html_editor_view_get_text_html_for_drafts (view);
+
+ e_html_editor_view_remove_embed_styles (view);
+ e_html_editor_selection_restore_caret_position (selection);
+
+ g_byte_array_append (data, (guint8 *) text, strlen (text));
+
+ g_free (text);
+
+ type = camel_content_type_new ("text", "html");
+ camel_content_type_set_param (type, "charset", "utf-8");
+ iconv_charset = camel_iconv_charset_name ("utf-8");
+
+ goto wrap_drafts_html;
+ }
+
/* Build the text/plain part. */
if (priv->mime_body) {
@@ -1235,11 +1264,14 @@ composer_build_message (EMsgComposer *composer,
} else {
gchar *text;
- gsize length;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
data = g_byte_array_new ();
- text = gtkhtml_editor_get_text_plain (editor, &length);
- g_byte_array_append (data, (guint8 *) text, (guint) length);
+ text = e_html_editor_view_get_text_plain (view);
+ g_byte_array_append (data, (guint8 *) text, strlen (text));
g_free (text);
type = camel_content_type_new ("text", "plain");
@@ -1252,6 +1284,7 @@ composer_build_message (EMsgComposer *composer,
}
}
+ wrap_drafts_html:
mem_stream = camel_stream_mem_new_with_byte_array (data);
stream = camel_stream_filter_new (mem_stream);
g_object_unref (mem_stream);
@@ -1298,25 +1331,27 @@ composer_build_message (EMsgComposer *composer,
* ...
*/
- if (flags & COMPOSER_FLAG_HTML_CONTENT) {
+ if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 &&
+ !(flags & COMPOSER_FLAG_SAVE_DRAFT)) {
gchar *text;
+ guint count;
gsize length;
gboolean pre_encode;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ GList *inline_images;
- clear_current_images (composer);
-
- if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
- gtkhtml_editor_run_command (editor, "save-data-on");
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ inline_images = e_html_editor_view_get_parts_for_inline_images (view);
data = g_byte_array_new ();
- text = gtkhtml_editor_get_text_html (editor, &length);
+ text = e_html_editor_view_get_text_html (view);
+ length = strlen (text);
g_byte_array_append (data, (guint8 *) text, (guint) length);
pre_encode = text_requires_quoted_printable (text, length);
g_free (text);
- if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
- gtkhtml_editor_run_command (editor, "save-data-off");
-
mem_stream = camel_stream_mem_new_with_byte_array (data);
stream = camel_stream_filter_new (mem_stream);
g_object_unref (mem_stream);
@@ -1366,7 +1401,9 @@ composer_build_message (EMsgComposer *composer,
/* If there are inlined images, construct a multipart/related
* containing the multipart/alternative and the images. */
- if (priv->current_images) {
+ count = g_list_length (inline_images);
+ if (count > 0) {
+ guint ii;
CamelMultipart *html_with_images;
html_with_images = camel_multipart_new ();
@@ -1385,8 +1422,12 @@ composer_build_message (EMsgComposer *composer,
g_object_unref (body);
- add_inlined_images (composer, html_with_images);
- clear_current_images (composer);
+ for (ii = 0; ii < count; ii++) {
+ CamelMimePart *part = g_list_nth_data (inline_images, ii);
+ camel_multipart_add_part (
+ html_with_images, part);
+ g_object_unref (part);
+ }
context->top_level_part =
CAMEL_DATA_WRAPPER (html_with_images);
@@ -1493,7 +1534,8 @@ use_top_signature (EMsgComposer *composer)
return top_signature;
}
-#define NO_SIGNATURE_TEXT \
+/* FIXME WEBKIT Nope....*/
+#define NO_SIGNATURE_TEXT \
"<!--+GtkHTML:<DATA class=\"ClueFlow\" " \
" key=\"signature\" " \
" value=\"1\">-->" \
@@ -1506,7 +1548,8 @@ set_editor_text (EMsgComposer *composer,
const gchar *text,
gboolean set_signature)
{
- gchar *body = NULL;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (text != NULL);
@@ -1527,20 +1570,23 @@ set_editor_text (EMsgComposer *composer,
/* "Edit as New Message" sets "priv->is_from_message".
* Always put the signature at the bottom for that case. */
+
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
if (!composer->priv->is_from_message && use_top_signature (composer)) {
+ gchar *body;
/* put marker to the top */
- body = g_strdup_printf ("<BR>" NO_SIGNATURE_TEXT "%s", text);
+ body = g_strdup_printf ("<BR>%s", text);
+ e_html_editor_view_set_text_html (view, body);
+ g_free (body);
} else {
- /* no marker => to the bottom */
- body = g_strdup_printf ("%s<BR>", text);
+ e_html_editor_view_set_text_html (view, text);
}
- gtkhtml_editor_set_text_html (GTKHTML_EDITOR (composer), body, -1);
-
if (set_signature)
e_composer_update_signature (composer);
- g_free (body);
}
/* Miscellaneous callbacks. */
@@ -1548,12 +1594,16 @@ set_editor_text (EMsgComposer *composer,
static void
attachment_store_changed_cb (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
/* Mark the editor as changed so it prompts about unsaved
* changes on close. */
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ if (editor) {
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
+ }
}
static void
@@ -1648,11 +1698,11 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
gint n_targets,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
- gboolean html_mode;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- html_mode = gtkhtml_editor_get_html_mode (editor);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
/* Order is important here to ensure common use cases are
* handled correctly. See GNOME bug #603715 for details. */
@@ -1663,7 +1713,7 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
}
/* Only paste HTML content in HTML mode. */
- if (html_mode) {
+ if (e_html_editor_view_get_html_mode (view)) {
if (e_targets_include_html (targets, n_targets)) {
e_composer_paste_html (composer, clipboard);
return;
@@ -1682,48 +1732,31 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
}
static void
-msg_composer_paste_clipboard_cb (EWebViewGtkHTML *web_view,
- EMsgComposer *composer)
+msg_composer_paste_primary_clipboard_cb (EHTMLEditorView *view,
+ EMsgComposer *composer)
{
GtkClipboard *clipboard;
- clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
gtk_clipboard_request_targets (
clipboard, (GtkClipboardTargetsReceivedFunc)
msg_composer_paste_clipboard_targets_cb, composer);
-
- g_signal_stop_emission_by_name (web_view, "paste-clipboard");
}
static void
-msg_composer_realize_gtkhtml_cb (GtkWidget *widget,
+msg_composer_paste_clipboard_cb (EHTMLEditorView *view,
EMsgComposer *composer)
{
- EAttachmentView *view;
- GtkTargetList *target_list;
- GtkTargetEntry *targets;
- gint n_targets;
-
- /* XXX GtkHTML doesn't set itself up as a drag destination until
- * it's realized, and we need to amend to its target list so
- * it will accept the same drag targets as the attachment bar.
- * Do this any earlier and GtkHTML will just overwrite us. */
-
- /* When redirecting a message, the message body is not
- * editable and therefore cannot be a drag destination. */
- if (!e_web_view_gtkhtml_get_editable (E_WEB_VIEW_GTKHTML (widget)))
- return;
-
- view = e_msg_composer_get_attachment_view (composer);
+ GtkClipboard *clipboard;
- target_list = e_attachment_view_get_target_list (view);
- targets = gtk_target_table_new_from_list (target_list, &n_targets);
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- target_list = gtk_drag_dest_get_target_list (widget);
- gtk_target_list_add_table (target_list, targets, n_targets);
+ gtk_clipboard_request_targets (
+ clipboard, (GtkClipboardTargetsReceivedFunc)
+ msg_composer_paste_clipboard_targets_cb, composer);
- gtk_target_table_free (targets, n_targets);
+ g_signal_stop_emission_by_name (view, "paste-clipboard");
}
static gboolean
@@ -1744,6 +1777,31 @@ msg_composer_drag_motion_cb (GtkWidget *widget,
return e_attachment_view_drag_motion (view, context, x, y, time);
}
+static gchar *
+next_uri (guchar **uri_list,
+ gint *len,
+ gint *list_len)
+{
+ guchar *uri, *begin;
+
+ begin = *uri_list;
+ *len = 0;
+ while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
+ (*uri_list) ++;
+ (*len) ++;
+ (*list_len) --;
+ }
+
+ uri = (guchar *) g_strndup ((gchar *) begin, *len);
+
+ while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
+ (*uri_list) ++;
+ (*list_len) --;
+ }
+
+ return (gchar *) uri;
+}
+
static void
msg_composer_drag_data_received_cb (GtkWidget *widget,
GdkDragContext *context,
@@ -1755,43 +1813,87 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
EMsgComposer *composer)
{
EAttachmentView *view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *html_editor_view;
+ EHTMLEditorSelection *editor_selection;
- /* HTML mode has a few special cases for drops... */
- if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer))) {
+ editor = e_msg_composer_get_editor (composer);
+ html_editor_view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (html_editor_view);
+ /* HTML mode has a few special cases for drops... */
+ if (e_html_editor_view_get_html_mode (html_editor_view)) {
/* If we're receiving an image, we want the image to be
* inserted in the message body. Let GtkHtml handle it. */
+ /* FIXME WebKit - how to reproduce this?
if (gtk_selection_data_targets_include_image (selection, TRUE))
return;
-
+ */
/* If we're receiving URIs and -all- the URIs point to
* image files, we want the image(s) to be inserted in
- * the message body. Let GtkHtml handle it. */
- if (e_composer_selection_is_image_uris (composer, selection))
- return;
- }
+ * the message body. */
+ if (e_composer_selection_is_image_uris (composer, selection)) {
+ const guchar *data;
+ gint length;
+ gint list_len, len;
+ gchar *uri;
+
+ data = gtk_selection_data_get_data (selection);
+ length = gtk_selection_data_get_length (selection);
+
+ if (!data || length < 0)
+ return;
+
+ list_len = length;
+ do {
+ uri = next_uri ((guchar **) &data, &len, &list_len);
+ e_html_editor_selection_insert_image (editor_selection, uri);
+ } while (list_len);
+ }
- view = e_msg_composer_get_attachment_view (composer);
+ if (e_composer_selection_is_base64_uris (composer, selection)) {
+ const guchar *data;
+ gint length;
+ gint list_len, len;
+ gchar *uri;
- /* Forward the data to the attachment view. Note that calling
- * e_attachment_view_drag_data_received() will not work because
- * that function only handles the case where all the other drag
- * handlers have failed. */
- e_attachment_paned_drag_data_received (
- E_ATTACHMENT_PANED (view),
- context, x, y, selection, info, time);
+ data = gtk_selection_data_get_data (selection);
+ length = gtk_selection_data_get_length (selection);
- /* Stop the signal from propagating to GtkHtml. */
+ if (!data || length < 0)
+ return;
+
+ list_len = length;
+ do {
+ uri = next_uri ((guchar **) &data, &len, &list_len);
+
+ e_html_editor_selection_insert_image (editor_selection, uri);
+ } while (list_len);
+ }
+ } else {
+ view = e_msg_composer_get_attachment_view (composer);
+
+ /* Forward the data to the attachment view. Note that calling
+ * e_attachment_view_drag_data_received() will not work because
+ * that function only handles the case where all the other drag
+ * handlers have failed. */
+ e_attachment_paned_drag_data_received (
+ E_ATTACHMENT_PANED (view),
+ context, x, y, selection, info, time);
+ }
+ /* Stop the signal from propagating */
g_signal_stop_emission_by_name (widget, "drag-data-received");
}
static void
msg_composer_notify_header_cb (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, TRUE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
}
static gboolean
@@ -1892,6 +1994,12 @@ msg_composer_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
+ case PROP_BUSY:
+ g_value_set_boolean (
+ value, e_msg_composer_is_busy (
+ E_MSG_COMPOSER (object)));
+ return;
+
case PROP_FOCUS_TRACKER:
g_value_set_object (
value, e_msg_composer_get_focus_tracker (
@@ -1951,30 +2059,71 @@ msg_composer_gallery_drag_data_get (GtkIconView *icon_view,
}
static void
+composer_notify_activity_cb (EActivityBar *activity_bar,
+ GParamSpec *pspec,
+ EMsgComposer *composer)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ gboolean editable;
+ gboolean busy;
+
+ busy = (e_activity_bar_get_activity (activity_bar) != NULL);
+
+ if (busy == composer->priv->busy)
+ return;
+
+ composer->priv->busy = busy;
+
+ if (busy)
+ e_msg_composer_save_focused_widget (composer);
+
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ if (busy) {
+ editable = webkit_web_view_get_editable (web_view);
+ webkit_web_view_set_editable (web_view, FALSE);
+ composer->priv->saved_editable = editable;
+ } else {
+ editable = composer->priv->saved_editable;
+ webkit_web_view_set_editable (web_view, editable);
+ }
+
+ g_object_notify (G_OBJECT (composer), "busy");
+
+ if (!busy)
+ e_msg_composer_restore_focus_on_composer (composer);
+}
+
+static void
msg_composer_constructed (GObject *object)
{
EShell *shell;
- GtkhtmlEditor *editor;
EMsgComposer *composer;
+ EActivityBar *activity_bar;
EAttachmentView *view;
EAttachmentStore *store;
EComposerHeaderTable *table;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *html_editor_view;
GtkUIManager *ui_manager;
GtkToggleAction *action;
GSettings *settings;
const gchar *id;
gboolean active;
- editor = GTKHTML_EDITOR (object);
composer = E_MSG_COMPOSER (object);
shell = e_msg_composer_get_shell (composer);
e_composer_private_constructed (composer);
- web_view = e_msg_composer_get_web_view (composer);
- ui_manager = gtkhtml_editor_get_ui_manager (editor);
+ editor = e_msg_composer_get_editor (composer);
+ html_editor_view = e_html_editor_get_view (editor);
+ ui_manager = e_html_editor_get_ui_manager (editor);
view = e_msg_composer_get_attachment_view (composer);
table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
@@ -2004,6 +2153,12 @@ msg_composer_constructed (GObject *object)
"/org/gnome/evolution/mail/composer-window/",
E_RESTORE_WINDOW_SIZE);
+ activity_bar = e_html_editor_get_activity_bar (editor);
+ g_signal_connect (
+ activity_bar, "notify::activity",
+ G_CALLBACK (composer_notify_activity_cb), composer);
+
+
/* Honor User Preferences */
/* FIXME This should be an EMsgComposer property. */
@@ -2016,21 +2171,21 @@ msg_composer_constructed (GObject *object)
/* Clipboard Support */
g_signal_connect (
- web_view, "paste-clipboard",
+ html_editor_view, "paste-clipboard",
G_CALLBACK (msg_composer_paste_clipboard_cb), composer);
- /* Drag-and-Drop Support */
-
g_signal_connect (
- web_view, "realize",
- G_CALLBACK (msg_composer_realize_gtkhtml_cb), composer);
+ html_editor_view, "paste-primary-clipboard",
+ G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer);
+
+ /* Drag-and-Drop Support */
g_signal_connect (
- web_view, "drag-motion",
+ html_editor_view, "drag-motion",
G_CALLBACK (msg_composer_drag_motion_cb), composer);
g_signal_connect (
- web_view, "drag-data-received",
+ html_editor_view, "drag-data-received",
G_CALLBACK (msg_composer_drag_data_received_cb), composer);
g_signal_connect (
@@ -2079,7 +2234,13 @@ msg_composer_constructed (GObject *object)
G_CALLBACK (attachment_store_changed_cb), composer);
/* Initialization may have tripped the "changed" state. */
- gtkhtml_editor_set_changed (editor, FALSE);
+ e_html_editor_view_set_changed (html_editor_view, FALSE);
+
+ gtk_drag_dest_set (
+ GTK_WIDGET (html_editor_view),
+ GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+ drag_dest_targets, G_N_ELEMENTS (drag_dest_targets),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
id = "org.gnome.evolution.composer";
e_plugin_ui_register_manager (ui_manager, id, composer);
@@ -2087,6 +2248,7 @@ msg_composer_constructed (GObject *object)
e_extensible_load_extensions (E_EXTENSIBLE (composer));
+ e_msg_composer_set_body_text (composer, "", TRUE);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object);
}
@@ -2119,14 +2281,19 @@ msg_composer_dispose (GObject *object)
static void
msg_composer_map (GtkWidget *widget)
{
+ EMsgComposer *composer;
EComposerHeaderTable *table;
GtkWidget *input_widget;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
const gchar *text;
/* Chain up to parent's map() method. */
GTK_WIDGET_CLASS (e_msg_composer_parent_class)->map (widget);
- table = e_msg_composer_get_header_table (E_MSG_COMPOSER (widget));
+ composer = E_MSG_COMPOSER (widget);
+ editor = e_msg_composer_get_editor (composer);
+ table = e_msg_composer_get_header_table (composer);
/* If the 'To' field is empty, focus it. */
input_widget =
@@ -2149,7 +2316,8 @@ msg_composer_map (GtkWidget *widget)
}
/* Jump to the editor as a last resort. */
- gtkhtml_editor_run_command (GTKHTML_EDITOR (widget), "grab-focus");
+ view = e_html_editor_get_view (editor);
+ gtk_widget_grab_focus (GTK_WIDGET (view));
}
static gboolean
@@ -2158,12 +2326,12 @@ msg_composer_key_press_event (GtkWidget *widget,
{
EMsgComposer *composer;
GtkWidget *input_widget;
- GtkhtmlEditor *editor;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (widget);
composer = E_MSG_COMPOSER (widget);
- web_view = e_msg_composer_get_web_view (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
input_widget =
e_composer_header_table_get_header (
@@ -2183,12 +2351,12 @@ msg_composer_key_press_event (GtkWidget *widget,
}
if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
- gtkhtml_editor_run_command (editor, "grab-focus");
+ gtk_widget_grab_focus (GTK_WIDGET (view));
return TRUE;
}
if (event->keyval == GDK_KEY_ISO_Left_Tab &&
- gtk_widget_is_focus (GTK_WIDGET (web_view))) {
+ gtk_widget_is_focus (GTK_WIDGET (view))) {
gtk_widget_grab_focus (input_widget);
return TRUE;
}
@@ -2198,160 +2366,6 @@ msg_composer_key_press_event (GtkWidget *widget,
key_press_event (widget, event);
}
-static void
-msg_composer_cut_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-msg_composer_copy_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-msg_composer_paste_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-msg_composer_select_all (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-msg_composer_command_before (GtkhtmlEditor *editor,
- const gchar *command)
-{
- EMsgComposer *composer;
- const gchar *data;
-
- composer = E_MSG_COMPOSER (editor);
-
- if (strcmp (command, "insert-paragraph") != 0)
- return;
-
- if (composer->priv->in_signature_insert)
- return;
-
- data = gtkhtml_editor_get_paragraph_data (editor, "orig");
- if (data != NULL && *data == '1') {
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
- return;
- };
-
- data = gtkhtml_editor_get_paragraph_data (editor, "signature");
- if (data != NULL && *data == '1') {
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
- }
-}
-
-static void
-msg_composer_command_after (GtkhtmlEditor *editor,
- const gchar *command)
-{
- EMsgComposer *composer;
- const gchar *data;
-
- composer = E_MSG_COMPOSER (editor);
-
- if (strcmp (command, "insert-paragraph") != 0)
- return;
-
- if (composer->priv->in_signature_insert)
- return;
-
- gtkhtml_editor_run_command (editor, "italic-off");
-
- data = gtkhtml_editor_get_paragraph_data (editor, "orig");
- if (data != NULL && *data == '1')
- e_msg_composer_reply_indent (composer);
- gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
-
- data = gtkhtml_editor_get_paragraph_data (editor, "signature");
- if (data == NULL || *data != '1')
- return;
-
- /* Clear the signature. */
- if (gtkhtml_editor_is_paragraph_empty (editor))
- gtkhtml_editor_set_paragraph_data (editor, "signature" ,"0");
-
- else if (gtkhtml_editor_is_previous_paragraph_empty (editor) &&
- gtkhtml_editor_run_command (editor, "cursor-backward")) {
-
- gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
- gtkhtml_editor_run_command (editor, "cursor-forward");
- }
-
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
-}
-
-static gchar *
-msg_composer_image_uri (GtkhtmlEditor *editor,
- const gchar *uri)
-{
- EMsgComposer *composer;
- GHashTable *hash_table;
- CamelMimePart *part;
- const gchar *cid;
-
- composer = E_MSG_COMPOSER (editor);
-
- hash_table = composer->priv->inline_images_by_url;
- part = g_hash_table_lookup (hash_table, uri);
-
- if (part == NULL && g_str_has_prefix (uri, "file:"))
- part = e_msg_composer_add_inline_image_from_file (
- composer, uri + 5);
-
- if (part == NULL && g_str_has_prefix (uri, "cid:")) {
- hash_table = composer->priv->inline_images;
- part = g_hash_table_lookup (hash_table, uri);
- }
-
- if (part == NULL)
- return NULL;
-
- composer->priv->current_images =
- g_list_prepend (composer->priv->current_images, part);
-
- cid = camel_mime_part_get_content_id (part);
- if (cid == NULL)
- return NULL;
-
- return g_strconcat ("cid:", cid, NULL);
-}
-
-static void
-msg_composer_object_deleted (GtkhtmlEditor *editor)
-{
- const gchar *data;
-
- if (!gtkhtml_editor_is_paragraph_empty (editor))
- return;
-
- data = gtkhtml_editor_get_paragraph_data (editor, "orig");
- if (data != NULL && *data == '1') {
- gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
- gtkhtml_editor_run_command (editor, "indent-zero");
- gtkhtml_editor_run_command (editor, "style-normal");
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
- gtkhtml_editor_run_command (editor, "insert-paragraph");
- gtkhtml_editor_run_command (editor, "delete-back");
- }
-
- data = gtkhtml_editor_get_paragraph_data (editor, "signature");
- if (data != NULL && *data == '1')
- gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
-}
-
static gboolean
msg_composer_presend (EMsgComposer *composer)
{
@@ -2359,34 +2373,6 @@ msg_composer_presend (EMsgComposer *composer)
return TRUE;
}
-static void
-msg_composer_submit_alert (EAlertSink *alert_sink,
- EAlert *alert)
-{
- EMsgComposerPrivate *priv;
- EAlertBar *alert_bar;
- GtkWidget *dialog;
- GtkWindow *parent;
-
- priv = E_MSG_COMPOSER_GET_PRIVATE (alert_sink);
-
- switch (e_alert_get_message_type (alert)) {
- case GTK_MESSAGE_INFO:
- case GTK_MESSAGE_WARNING:
- case GTK_MESSAGE_ERROR:
- alert_bar = E_ALERT_BAR (priv->alert_bar);
- e_alert_bar_add_alert (alert_bar, alert);
- break;
-
- default:
- parent = GTK_WINDOW (alert_sink);
- dialog = e_alert_dialog_new (parent, alert);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
- break;
- }
-}
-
static gboolean
msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
GValue *return_accu,
@@ -2402,12 +2388,27 @@ msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
return v_boolean;
}
+/**
+ * e_msg_composer_is_busy:
+ * @composer: an #EMsgComposer
+ *
+ * Returns %TRUE only while an #EActivity is in progress.
+ *
+ * Returns: whether @composer is busy
+ **/
+gboolean
+e_msg_composer_is_busy (EMsgComposer *composer)
+{
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+
+ return composer->priv->busy;
+}
+
static void
e_msg_composer_class_init (EMsgComposerClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
- GtkhtmlEditorClass *editor_class;
g_type_class_add_private (class, sizeof (EMsgComposerPrivate));
@@ -2422,21 +2423,21 @@ e_msg_composer_class_init (EMsgComposerClass *class)
widget_class->map = msg_composer_map;
widget_class->key_press_event = msg_composer_key_press_event;
- editor_class = GTKHTML_EDITOR_CLASS (class);
- editor_class->cut_clipboard = msg_composer_cut_clipboard;
- editor_class->copy_clipboard = msg_composer_copy_clipboard;
- editor_class->paste_clipboard = msg_composer_paste_clipboard;
- editor_class->select_all = msg_composer_select_all;
- editor_class->command_before = msg_composer_command_before;
- editor_class->command_after = msg_composer_command_after;
- editor_class->image_uri = msg_composer_image_uri;
- editor_class->link_clicked = NULL; /* EWebView handles this */
- editor_class->object_deleted = msg_composer_object_deleted;
-
class->presend = msg_composer_presend;
g_object_class_install_property (
object_class,
+ PROP_BUSY,
+ g_param_spec_boolean (
+ "busy",
+ "Busy",
+ "Whether an activity is in progress",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
PROP_FOCUS_TRACKER,
g_param_spec_object (
"focus-tracker",
@@ -2512,15 +2513,11 @@ e_msg_composer_class_init (EMsgComposerClass *class)
}
static void
-e_msg_composer_alert_sink_init (EAlertSinkInterface *iface)
-{
- iface->submit_alert = msg_composer_submit_alert;
-}
-
-static void
e_msg_composer_init (EMsgComposer *composer)
{
composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
+
+ composer->priv->editor = g_object_ref_sink (e_html_editor_new ());
}
/**
@@ -2538,7 +2535,23 @@ e_msg_composer_new (EShell *shell)
return g_object_new (
E_TYPE_MSG_COMPOSER,
- "html", e_web_view_gtkhtml_new (), "shell", shell, NULL);
+ "shell", shell, NULL);
+}
+
+/**
+ * e_msg_composer_get_editor:
+ * @composer: an #EMsgComposer
+ *
+ * Returns @composer's internal #EHTMLEditor instance.
+ *
+ * Returns: an #EHTMLEditor
+ **/
+EHTMLEditor *
+e_msg_composer_get_editor (EMsgComposer *composer)
+{
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+ return composer->priv->editor;
}
EFocusTracker *
@@ -2581,12 +2594,16 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
{
CamelContentType *content_type;
CamelDataWrapper *wrapper;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
if (!mime_part)
return;
content_type = camel_mime_part_get_content_type (mime_part);
wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
if (CAMEL_IS_MULTIPART (wrapper)) {
/* another layer of multipartness... */
@@ -2596,10 +2613,10 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
} else if (just_inlines) {
if (camel_mime_part_get_content_id (mime_part) ||
camel_mime_part_get_content_location (mime_part))
- e_msg_composer_add_inline_image_from_mime_part (
- composer, mime_part);
+ e_html_editor_view_add_inline_image_from_mime_part (
+ view, mime_part);
} else if (related && camel_content_type_is (content_type, "image", "*")) {
- e_msg_composer_add_inline_image_from_mime_part (composer, mime_part);
+ e_html_editor_view_add_inline_image_from_mime_part (view, mime_part);
} else if (camel_content_type_is (content_type, "text", "*") &&
camel_mime_part_get_filename (mime_part) == NULL) {
/* Do nothing if this is a text/anything without a
@@ -2988,16 +3005,40 @@ handle_multipart (EMsgComposer *composer,
/* Since the first part is not multipart/alternative,
* this must be the body. */
- html = emcu_part_to_html (
- composer, mime_part, &length, keep_signature, cancellable);
- if (html)
- e_msg_composer_set_pending_body (composer, html, length);
+
+ /* If we are opening message from Drafts */
+ if (composer->priv->is_from_draft) {
+ /* Extract the body */
+ CamelDataWrapper *dw;
+
+ dw = camel_medium_get_content ((CamelMedium *) mime_part);
+ if (dw) {
+ CamelStream *mem = camel_stream_mem_new ();
+ GByteArray *bytes;
+
+ camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
+ camel_stream_close (mem, cancellable, NULL);
+
+ bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
+ if (bytes && bytes->len)
+ html = g_strndup ((const gchar *) bytes->data, bytes->len);
+
+ g_object_unref (mem);
+ }
+ } else {
+ html = emcu_part_to_html (
+ composer, mime_part, &length, keep_signature, cancellable);
+ }
+ e_msg_composer_set_pending_body (composer, html, length);
} else if (camel_mime_part_get_content_id (mime_part) ||
camel_mime_part_get_content_location (mime_part)) {
/* special in-line attachment */
- e_msg_composer_add_inline_image_from_mime_part (
- composer, mime_part);
+ EHTMLEditor *editor;
+
+ editor = e_msg_composer_get_editor (composer);
+ e_html_editor_view_add_inline_image_from_mime_part (
+ e_html_editor_get_view (editor), mime_part);
} else {
/* normal attachment */
@@ -3009,28 +3050,46 @@ handle_multipart (EMsgComposer *composer,
static void
set_signature_gui (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *nodes;
EComposerHeaderTable *table;
EMailSignatureComboBox *combo_box;
- const gchar *data;
gchar *uid;
+ gulong ii, length;
- editor = GTKHTML_EDITOR (composer);
table = e_msg_composer_get_header_table (composer);
combo_box = e_composer_header_table_get_signature_combo_box (table);
- if (!gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1"))
- return;
-
- data = gtkhtml_editor_get_paragraph_data (editor, "signature_name");
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
- if (!g_str_has_prefix (data, "uid:"))
- return;
+ uid = NULL;
+ nodes = webkit_dom_document_get_elements_by_class_name (
+ document, "-x-evo-signature");
+ length = webkit_dom_node_list_get_length (nodes);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node;
+ gchar *id;
+
+ node = webkit_dom_node_list_item (nodes, ii);
+ id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node));
+ if (id && (strlen (id) == 1) && (*id == '1')) {
+ uid = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "name");
+ g_free (id);
+ break;
+ }
+ g_free (id);
+ }
/* The combo box active ID is the signature's ESource UID. */
- uid = e_composer_decode_clue_value (data + 4);
- gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
- g_free (uid);
+ if (uid != NULL) {
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
+ g_free (uid);
+ }
}
static void
@@ -3100,7 +3159,7 @@ e_msg_composer_new_with_message (EShell *shell,
{
CamelInternetAddress *to, *cc, *bcc;
GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL;
- const gchar *format, *subject;
+ const gchar *format, *subject, *composer_mode;
EDestination **Tov, **Ccv, **Bccv;
GHashTable *auto_cc, *auto_bcc;
CamelContentType *content_type;
@@ -3110,6 +3169,8 @@ e_msg_composer_new_with_message (EShell *shell,
EMsgComposerPrivate *priv;
EComposerHeaderTable *table;
ESource *source = NULL;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GtkToggleAction *action;
struct _camel_header_raw *xev;
gchar *identity_uid;
@@ -3131,7 +3192,9 @@ e_msg_composer_new_with_message (EShell *shell,
composer = e_msg_composer_new (shell);
priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
+ editor = e_msg_composer_get_editor (composer);
table = e_msg_composer_get_header_table (composer);
+ view = e_html_editor_get_view (editor);
if (postto) {
e_composer_header_table_set_post_to_list (table, postto);
@@ -3250,6 +3313,13 @@ e_msg_composer_new_with_message (EShell *shell,
/* Restore the format editing preference */
format = camel_medium_get_header (
CAMEL_MEDIUM (message), "X-Evolution-Format");
+
+ composer_mode = camel_medium_get_header (
+ CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode");
+
+ if (composer_mode && *composer_mode)
+ composer->priv->is_from_draft = TRUE;
+
if (format != NULL) {
gchar **flags;
@@ -3259,11 +3329,21 @@ e_msg_composer_new_with_message (EShell *shell,
flags = g_strsplit (format, ", ", 0);
for (i = 0; flags[i]; i++) {
if (g_ascii_strcasecmp (flags[i], "text/html") == 0) {
- gtkhtml_editor_set_html_mode (
- GTKHTML_EDITOR (composer), TRUE);
+ if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
+ e_html_editor_view_set_html_mode (
+ view, TRUE);
+ } else {
+ e_html_editor_view_set_html_mode (
+ view, FALSE);
+ }
} else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
- gtkhtml_editor_set_html_mode (
- GTKHTML_EDITOR (composer), FALSE);
+ if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
+ e_html_editor_view_set_html_mode (
+ view, TRUE);
+ } else {
+ e_html_editor_view_set_html_mode (
+ view, FALSE);
+ }
} else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
gtk_toggle_action_set_active (action, TRUE);
@@ -3367,14 +3447,35 @@ e_msg_composer_new_with_message (EShell *shell,
ACTION (SMIME_ENCRYPT)), TRUE);
}
- html = emcu_part_to_html (
- composer, CAMEL_MIME_PART (message),
- &length, keep_signature, cancellable);
- if (html)
- e_msg_composer_set_pending_body (composer, html, length);
+ /* If we are opening message from Drafts */
+ if (composer->priv->is_from_draft) {
+ /* Extract the body */
+ CamelDataWrapper *dw;
+
+ dw = camel_medium_get_content ((CamelMedium *) mime_part);
+ if (dw) {
+ CamelStream *mem = camel_stream_mem_new ();
+ GByteArray *bytes;
+
+ camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
+ camel_stream_close (mem, cancellable, NULL);
+
+ bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
+ if (bytes && bytes->len)
+ html = g_strndup ((const gchar *) bytes->data, bytes->len);
+
+ g_object_unref (mem);
+ }
+ } else {
+ html = emcu_part_to_html (
+ composer, CAMEL_MIME_PART (message),
+ &length, keep_signature, cancellable);
+ }
+ e_msg_composer_set_pending_body (composer, html, length);
}
priv->is_from_message = TRUE;
+ priv->set_signature_from_message = TRUE;
/* We wait until now to set the body text because we need to
* ensure that the attachment bar has all the attachments before
@@ -3403,7 +3504,8 @@ e_msg_composer_new_redirect (EShell *shell,
{
EMsgComposer *composer;
EComposerHeaderTable *table;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
const gchar *subject;
g_return_val_if_fail (E_IS_SHELL (shell), NULL);
@@ -3421,8 +3523,9 @@ e_msg_composer_new_redirect (EShell *shell,
e_composer_header_table_set_identity_uid (table, identity_uid);
e_composer_header_table_set_subject (table, subject);
- web_view = e_msg_composer_get_web_view (composer);
- e_web_view_gtkhtml_set_editable (web_view, FALSE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
return composer;
}
@@ -3473,30 +3576,6 @@ e_msg_composer_get_shell (EMsgComposer *composer)
return E_SHELL (composer->priv->shell);
}
-/**
- * e_msg_composer_get_web_view:
- * @composer: an #EMsgComposer
- *
- * Returns the #EWebView widget in @composer.
- *
- * Returns: the #EWebView
- **/
-EWebViewGtkHTML *
-e_msg_composer_get_web_view (EMsgComposer *composer)
-{
- GtkHTML *html;
- GtkhtmlEditor *editor;
-
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
-
- /* This is a convenience function to avoid
- * repeating this awkwardness everywhere */
- editor = GTKHTML_EDITOR (composer);
- html = gtkhtml_editor_get_html (editor);
-
- return E_WEB_VIEW_GTKHTML (html);
-}
-
static void
msg_composer_send_cb (EMsgComposer *composer,
GAsyncResult *result,
@@ -3504,7 +3583,8 @@ msg_composer_send_cb (EMsgComposer *composer,
{
CamelMimeMessage *message;
EAlertSink *alert_sink;
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GError *error = NULL;
alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3536,8 +3616,9 @@ msg_composer_send_cb (EMsgComposer *composer,
g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
/* The callback can set editor 'changed' if anything failed. */
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, FALSE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
g_signal_emit (
composer, signals[SEND], 0,
@@ -3557,9 +3638,8 @@ msg_composer_send_cb (EMsgComposer *composer,
void
e_msg_composer_send (EMsgComposer *composer)
{
+ EHTMLEditor *editor;
AsyncContext *context;
- EAlertSink *alert_sink;
- EActivityBar *activity_bar;
GCancellable *cancellable;
gboolean proceed_with_send = TRUE;
@@ -3573,18 +3653,12 @@ e_msg_composer_send (EMsgComposer *composer)
return;
}
- context = g_slice_new0 (AsyncContext);
- context->activity = e_composer_activity_new (composer);
-
- alert_sink = E_ALERT_SINK (composer);
- e_activity_set_alert_sink (context->activity, alert_sink);
+ editor = e_msg_composer_get_editor (composer);
- cancellable = camel_operation_new ();
- e_activity_set_cancellable (context->activity, cancellable);
- g_object_unref (cancellable);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_html_editor_new_activity (editor);
- activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
- e_activity_bar_set_activity (activity_bar, context->activity);
+ cancellable = e_activity_get_cancellable (context->activity);
e_msg_composer_get_message (
composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3599,7 +3673,8 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
{
CamelMimeMessage *message;
EAlertSink *alert_sink;
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GError *error = NULL;
alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3640,8 +3715,9 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
/* The callback can set editor 'changed' if anything failed. */
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, FALSE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, FALSE);
g_signal_emit (
composer, signals[SAVE_TO_DRAFTS],
@@ -3666,25 +3742,18 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
void
e_msg_composer_save_to_drafts (EMsgComposer *composer)
{
+ EHTMLEditor *editor;
AsyncContext *context;
- EAlertSink *alert_sink;
- EActivityBar *activity_bar;
GCancellable *cancellable;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- context = g_slice_new0 (AsyncContext);
- context->activity = e_composer_activity_new (composer);
-
- alert_sink = E_ALERT_SINK (composer);
- e_activity_set_alert_sink (context->activity, alert_sink);
+ editor = e_msg_composer_get_editor (composer);
- cancellable = camel_operation_new ();
- e_activity_set_cancellable (context->activity, cancellable);
- g_object_unref (cancellable);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_html_editor_new_activity (editor);
- activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
- e_activity_bar_set_activity (activity_bar, context->activity);
+ cancellable = e_activity_get_cancellable (context->activity);
e_msg_composer_get_message_draft (
composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3699,7 +3768,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
{
CamelMimeMessage *message;
EAlertSink *alert_sink;
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GError *error = NULL;
alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3734,9 +3804,9 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
async_context_free (context);
- /* XXX This should be elsewhere. */
- editor = GTKHTML_EDITOR (composer);
- gtkhtml_editor_set_changed (editor, FALSE);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, FALSE);
}
/**
@@ -3748,9 +3818,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
void
e_msg_composer_save_to_outbox (EMsgComposer *composer)
{
+ EHTMLEditor *editor;
AsyncContext *context;
- EAlertSink *alert_sink;
- EActivityBar *activity_bar;
GCancellable *cancellable;
gboolean proceed_with_save = TRUE;
@@ -3762,18 +3831,12 @@ e_msg_composer_save_to_outbox (EMsgComposer *composer)
if (!proceed_with_save)
return;
- context = g_slice_new0 (AsyncContext);
- context->activity = e_composer_activity_new (composer);
+ editor = e_msg_composer_get_editor (composer);
- alert_sink = E_ALERT_SINK (composer);
- e_activity_set_alert_sink (context->activity, alert_sink);
-
- cancellable = camel_operation_new ();
- e_activity_set_cancellable (context->activity, cancellable);
- g_object_unref (cancellable);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_html_editor_new_activity (editor);
- activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
- e_activity_bar_set_activity (activity_bar, context->activity);
+ cancellable = e_activity_get_cancellable (context->activity);
e_msg_composer_get_message (
composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3835,26 +3898,19 @@ void
e_msg_composer_print (EMsgComposer *composer,
GtkPrintOperationAction print_action)
{
+ EHTMLEditor *editor;
AsyncContext *context;
- EAlertSink *alert_sink;
- EActivityBar *activity_bar;
GCancellable *cancellable;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ editor = e_msg_composer_get_editor (composer);
+
context = g_slice_new0 (AsyncContext);
- context->activity = e_composer_activity_new (composer);
+ context->activity = e_html_editor_new_activity (editor);
context->print_action = print_action;
- alert_sink = E_ALERT_SINK (composer);
- e_activity_set_alert_sink (context->activity, alert_sink);
-
- cancellable = camel_operation_new ();
- e_activity_set_cancellable (context->activity, cancellable);
- g_object_unref (cancellable);
-
- activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
- e_activity_bar_set_activity (activity_bar, context->activity);
+ cancellable = e_activity_get_cancellable (context->activity);
e_msg_composer_get_message_print (
composer, G_PRIORITY_DEFAULT, cancellable,
@@ -4226,15 +4282,21 @@ e_msg_composer_set_body (EMsgComposer *composer,
{
EMsgComposerPrivate *priv = composer->priv;
EComposerHeaderTable *table;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
ESource *source;
const gchar *identity_uid;
gchar *buff;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
table = e_msg_composer_get_header_table (composer);
+ /* Disable signature */
+ priv->disable_signature = TRUE;
+
identity_uid = e_composer_header_table_get_identity_uid (table);
source = e_composer_header_table_ref_source (table, identity_uid);
@@ -4245,10 +4307,8 @@ e_msg_composer_set_body (EMsgComposer *composer,
set_editor_text (composer, buff, FALSE);
g_free (buff);
- gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (composer), FALSE);
-
- web_view = e_msg_composer_get_web_view (composer);
- e_web_view_gtkhtml_set_editable (web_view, FALSE);
+ e_html_editor_view_set_html_mode (view, FALSE);
+ webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
g_free (priv->mime_body);
priv->mime_body = g_strdup (body);
@@ -4459,109 +4519,12 @@ e_msg_composer_attach (EMsgComposer *composer,
attachment = e_attachment_new ();
e_attachment_set_mime_part (attachment, mime_part);
e_attachment_store_add_attachment (store, attachment);
- e_attachment_load (attachment, NULL);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, composer);
g_object_unref (attachment);
}
-/**
- * e_msg_composer_add_inline_image_from_file:
- * @composer: a composer object
- * @filename: the name of the file containing the image
- *
- * This reads in the image in @filename and adds it to @composer
- * as an inline image, to be wrapped in a multipart/related.
- *
- * Returns: the newly-created CamelMimePart (which must be reffed
- * if the caller wants to keep its own reference), or %NULL on error.
- **/
-CamelMimePart *
-e_msg_composer_add_inline_image_from_file (EMsgComposer *composer,
- const gchar *filename)
-{
- gchar *mime_type, *cid, *url, *name, *dec_file_name;
- CamelStream *stream;
- CamelDataWrapper *wrapper;
- CamelMimePart *part;
- EMsgComposerPrivate *p = composer->priv;
-
- dec_file_name = g_strdup (filename);
- camel_url_decode (dec_file_name);
-
- if (!g_file_test (dec_file_name, G_FILE_TEST_IS_REGULAR))
- return NULL;
-
- stream = camel_stream_fs_new_with_name (
- dec_file_name, O_RDONLY, 0, NULL);
- if (!stream)
- return NULL;
-
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream_sync (
- wrapper, stream, NULL, NULL);
- g_object_unref (CAMEL_OBJECT (stream));
-
- mime_type = e_util_guess_mime_type (dec_file_name, TRUE);
- if (mime_type == NULL)
- mime_type = g_strdup ("application/octet-stream");
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
-
- part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
- g_object_unref (wrapper);
-
- cid = camel_header_msgid_generate ();
- camel_mime_part_set_content_id (part, cid);
- name = g_path_get_basename (dec_file_name);
- camel_mime_part_set_filename (part, name);
- g_free (name);
- camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
-
- url = g_strdup_printf ("file:%s", dec_file_name);
- g_hash_table_insert (p->inline_images_by_url, url, part);
-
- url = g_strdup_printf ("cid:%s", cid);
- g_hash_table_insert (p->inline_images, url, part);
- g_free (cid);
-
- g_free (dec_file_name);
-
- return part;
-}
-
-/**
- * e_msg_composer_add_inline_image_from_mime_part:
- * @composer: a composer object
- * @part: a CamelMimePart containing image data
- *
- * This adds the mime part @part to @composer as an inline image, to
- * be wrapped in a multipart/related.
- **/
-void
-e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
- CamelMimePart *part)
-{
- gchar *url;
- const gchar *location, *cid;
- EMsgComposerPrivate *p = composer->priv;
-
- cid = camel_mime_part_get_content_id (part);
- if (!cid) {
- camel_mime_part_set_content_id (part, NULL);
- cid = camel_mime_part_get_content_id (part);
- }
-
- url = g_strdup_printf ("cid:%s", cid);
- g_hash_table_insert (p->inline_images, url, part);
- g_object_ref (part);
-
- location = camel_mime_part_get_content_location (part);
- if (location != NULL)
- g_hash_table_insert (
- p->inline_images_by_url,
- g_strdup (location), part);
-}
-
static void
composer_get_message_ready (EMsgComposer *composer,
GAsyncResult *result,
@@ -4604,16 +4567,21 @@ e_msg_composer_get_message (EMsgComposer *composer,
GSimpleAsyncResult *simple;
GtkAction *action;
ComposerFlags flags = 0;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
simple = g_simple_async_result_new (
G_OBJECT (composer), callback,
user_data, e_msg_composer_get_message);
g_simple_async_result_set_check_cancellable (simple, cancellable);
- if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+ if (e_html_editor_view_get_html_mode (view))
flags |= COMPOSER_FLAG_HTML_CONTENT;
action = ACTION (PRIORITIZE_MESSAGE);
@@ -4730,8 +4698,10 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GSimpleAsyncResult *simple;
- ComposerFlags flags = COMPOSER_FLAG_DRAFT;
+ ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
GtkAction *action;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
@@ -4742,8 +4712,13 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
g_simple_async_result_set_check_cancellable (simple, cancellable);
- if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
- flags |= COMPOSER_FLAG_HTML_CONTENT;
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ /* We need to remember composer mode */
+ if (e_html_editor_view_get_html_mode (view))
+ flags |= COMPOSER_FLAG_HTML_MODE;
+ /* We want to save HTML content everytime when we save as draft */
+ flags |= COMPOSER_FLAG_SAVE_DRAFT;
action = ACTION (PRIORITIZE_MESSAGE);
if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
@@ -4872,17 +4847,19 @@ e_msg_composer_get_reply_to (EMsgComposer *composer)
GByteArray *
e_msg_composer_get_raw_message_text (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GByteArray *array;
gchar *text;
- gsize length;
g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
array = g_byte_array_new ();
- editor = GTKHTML_EDITOR (composer);
- text = gtkhtml_editor_get_text_plain (editor, &length);
- g_byte_array_append (array, (guint8 *) text, (guint) length);
+ text = e_html_editor_view_get_text_plain (view);
+ g_byte_array_append (array, (guint8 *) text, strlen (text));
g_free (text);
return array;
@@ -4915,22 +4892,24 @@ e_msg_composer_can_close (EMsgComposer *composer,
gboolean can_save_draft)
{
gboolean res = FALSE;
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
EComposerHeaderTable *table;
GdkWindow *window;
GtkWidget *widget;
const gchar *subject, *message_name;
gint response;
- editor = GTKHTML_EDITOR (composer);
widget = GTK_WIDGET (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
/* this means that there is an async operation running,
* in which case the composer cannot be closed */
if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
return FALSE;
- if (!gtkhtml_editor_get_changed (editor))
+ if (!e_html_editor_view_get_changed (view))
return TRUE;
window = gtk_widget_get_window (widget);
@@ -4968,32 +4947,6 @@ e_msg_composer_can_close (EMsgComposer *composer,
return res;
}
-void
-e_msg_composer_reply_indent (EMsgComposer *composer)
-{
- GtkhtmlEditor *editor;
-
- g_return_if_fail (E_IS_MSG_COMPOSER (composer));
-
- editor = GTKHTML_EDITOR (composer);
-
- if (!gtkhtml_editor_is_paragraph_empty (editor)) {
- if (gtkhtml_editor_is_previous_paragraph_empty (editor))
- gtkhtml_editor_run_command (editor, "cursor-backward");
- else {
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
- gtkhtml_editor_run_command (editor, "insert-paragraph");
- return;
- }
- }
-
- gtkhtml_editor_run_command (editor, "style-normal");
- gtkhtml_editor_run_command (editor, "indent-zero");
- gtkhtml_editor_run_command (editor, "text-default-color");
- gtkhtml_editor_run_command (editor, "italic-off");
-}
-
EComposerHeaderTable *
e_msg_composer_get_header_table (EMsgComposer *composer)
{
@@ -5010,76 +4963,101 @@ e_msg_composer_get_attachment_view (EMsgComposer *composer)
return E_ATTACHMENT_VIEW (composer->priv->attachment_paned);
}
-GList *
-e_load_spell_languages (void)
+void
+e_save_spell_languages (const GList *spell_dicts)
{
GSettings *settings;
- GList *spell_languages = NULL;
- gchar **strv;
- gint ii;
+ GPtrArray *lang_array;
+
+ /* Build a list of spell check language codes. */
+ lang_array = g_ptr_array_new ();
+ while (spell_dicts != NULL) {
+ ESpellDictionary *dict = spell_dicts->data;
+ const gchar *language_code;
+
+ language_code = e_spell_dictionary_get_code (dict);
+ g_ptr_array_add (lang_array, (gpointer) language_code);
+
+ spell_dicts = g_list_next (spell_dicts);
+ }
+
+ g_ptr_array_add (lang_array, NULL);
- /* Ask GSettings for a list of spell check language codes. */
+ /* Save the language codes to GSettings. */
settings = g_settings_new ("org.gnome.evolution.mail");
- strv = g_settings_get_strv (settings, "composer-spell-languages");
+ g_settings_set_strv (
+ settings, "composer-spell-languages",
+ (const gchar * const *) lang_array->pdata);
g_object_unref (settings);
- /* Convert the codes to spell language structs. */
- for (ii = 0; strv[ii] != NULL; ii++) {
- gchar *language_code = strv[ii];
- const GtkhtmlSpellLanguage *language;
+ g_ptr_array_free (lang_array, TRUE);
+}
- language = gtkhtml_spell_language_lookup (language_code);
- if (language != NULL)
- spell_languages = g_list_prepend (
- spell_languages, (gpointer) language);
- }
+void
+e_msg_composer_is_from_new_message (EMsgComposer *composer,
+ gboolean is_from_new_message)
+{
+ g_return_if_fail (composer != NULL);
+
+ composer->priv->is_from_new_message = is_from_new_message;
+}
+
+void
+e_msg_composer_save_focused_widget (EMsgComposer *composer)
+{
+ GtkWidget *widget;
- g_strfreev (strv);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- spell_languages = g_list_reverse (spell_languages);
+ widget = gtk_window_get_focus (GTK_WINDOW (composer));
+ composer->priv->focused_entry = widget;
- /* Pick a default spell language if it came back empty. */
- if (spell_languages == NULL) {
- const GtkhtmlSpellLanguage *language;
+ if (E_IS_HTML_EDITOR_VIEW (widget)) {
+ EHTMLEditorSelection *selection;
- language = gtkhtml_spell_language_lookup (NULL);
+ selection = e_html_editor_view_get_selection (
+ E_HTML_EDITOR_VIEW (widget));
- if (language) {
- spell_languages = g_list_prepend (
- spell_languages, (gpointer) language);
- }
+ e_html_editor_selection_save (selection);
}
- return spell_languages;
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_get_selection_bounds (
+ GTK_EDITABLE (widget),
+ &composer->priv->focused_entry_selection_start,
+ &composer->priv->focused_entry_selection_end);
+ }
}
void
-e_save_spell_languages (GList *spell_languages)
+e_msg_composer_restore_focus_on_composer (EMsgComposer *composer)
{
- GSettings *settings;
- GPtrArray *lang_array;
+ GtkWidget *widget = composer->priv->focused_entry;
- /* Build a list of spell check language codes. */
- lang_array = g_ptr_array_new ();
- while (spell_languages != NULL) {
- const GtkhtmlSpellLanguage *language;
- const gchar *language_code;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- language = spell_languages->data;
- language_code = gtkhtml_spell_language_get_code (language);
- g_ptr_array_add (lang_array, (gpointer) language_code);
+ if (!widget)
+ return;
+
+ gtk_window_set_focus (GTK_WINDOW (composer), widget);
- spell_languages = g_list_next (spell_languages);
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_select_region (
+ GTK_EDITABLE (widget),
+ composer->priv->focused_entry_selection_start,
+ composer->priv->focused_entry_selection_end);
}
- g_ptr_array_add (lang_array, NULL);
+ if (E_IS_HTML_EDITOR_VIEW (widget)) {
+ EHTMLEditorSelection *selection;
- /* Save the language codes to GSettings. */
- settings = g_settings_new ("org.gnome.evolution.mail");
- g_settings_set_strv (
- settings, "composer-spell-languages",
- (const gchar * const *) lang_array->pdata);
- g_object_unref (settings);
+ e_html_editor_view_force_spell_check (E_HTML_EDITOR_VIEW (widget));
- g_ptr_array_free (lang_array, TRUE);
+ selection = e_html_editor_view_get_selection (
+ E_HTML_EDITOR_VIEW (widget));
+
+ e_html_editor_selection_restore (selection);
+ }
+
+ composer->priv->focused_entry = NULL;
}
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index 1fbc1043fd..f6990415f7 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -24,7 +24,6 @@
#define E_MSG_COMPOSER_H
#include <camel/camel.h>
-#include <gtkhtml-editor.h>
#include <libebook/libebook.h>
#include <shell/e-shell.h>
@@ -57,12 +56,12 @@ typedef struct _EMsgComposerClass EMsgComposerClass;
typedef struct _EMsgComposerPrivate EMsgComposerPrivate;
struct _EMsgComposer {
- GtkhtmlEditor parent;
+ GtkWindow parent;
EMsgComposerPrivate *priv;
};
struct _EMsgComposerClass {
- GtkhtmlEditorClass parent_class;
+ GtkWindowClass parent_class;
/* Signals */
gboolean (*presend) (EMsgComposer *composer);
@@ -93,12 +92,11 @@ EMsgComposer * e_msg_composer_new_redirect (EShell *shell,
CamelMimeMessage *message,
const gchar *identity_uid,
GCancellable *cancellable);
+EHTMLEditor * e_msg_composer_get_editor (EMsgComposer *composer);
EFocusTracker * e_msg_composer_get_focus_tracker
(EMsgComposer *composer);
CamelSession * e_msg_composer_ref_session (EMsgComposer *composer);
EShell * e_msg_composer_get_shell (EMsgComposer *composer);
-EWebViewGtkHTML *
- e_msg_composer_get_web_view (EMsgComposer *composer);
void e_msg_composer_send (EMsgComposer *composer);
void e_msg_composer_save_to_drafts (EMsgComposer *composer);
@@ -131,12 +129,6 @@ void e_msg_composer_set_source_headers
CamelMessageFlags flags);
void e_msg_composer_attach (EMsgComposer *composer,
CamelMimePart *mime_part);
-CamelMimePart * e_msg_composer_add_inline_image_from_file
- (EMsgComposer *composer,
- const gchar *filename);
-void e_msg_composer_add_inline_image_from_mime_part
- (EMsgComposer *composer,
- CamelMimePart *part);
void e_msg_composer_get_message (EMsgComposer *composer,
gint io_priority,
GCancellable *cancellable,
@@ -175,8 +167,6 @@ CamelInternetAddress *
CamelInternetAddress *
e_msg_composer_get_reply_to (EMsgComposer *composer);
-void e_msg_composer_clear_inlined_table
- (EMsgComposer *composer);
void e_msg_composer_add_message_attachments
(EMsgComposer *composer,
CamelMimeMessage *message,
@@ -186,8 +176,6 @@ void e_msg_composer_request_close (EMsgComposer *composer);
gboolean e_msg_composer_can_close (EMsgComposer *composer,
gboolean can_save_draft);
-void e_msg_composer_reply_indent (EMsgComposer *composer);
-
EComposerHeaderTable *
e_msg_composer_get_header_table (EMsgComposer *composer);
EAttachmentView *
@@ -198,9 +186,15 @@ GByteArray * e_msg_composer_get_raw_message_text
gboolean e_msg_composer_is_exiting (EMsgComposer *composer);
-GList * e_load_spell_languages (void);
-void e_save_spell_languages (GList *spell_languages);
-
+void e_save_spell_languages (const GList *spell_languages);
+void e_msg_composer_is_from_new_message
+ (EMsgComposer *composer,
+ gboolean is_from_new_message);
+void e_msg_composer_save_focused_widget
+ (EMsgComposer *composer);
+void e_msg_composer_restore_focus_on_composer
+ (EMsgComposer *composer);
+gboolean e_msg_composer_is_busy (EMsgComposer *composer);
G_END_DECLS
#endif /* E_MSG_COMPOSER_H */