aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--addressbook/gui/contact-editor/Makefile.am6
-rw-r--r--addressbook/gui/contact-list-editor/Makefile.am6
-rw-r--r--addressbook/gui/widgets/Makefile.am8
-rw-r--r--addressbook/gui/widgets/eab-contact-formatter.c8
-rw-r--r--addressbook/importers/Makefile.am8
-rw-r--r--addressbook/printing/Makefile.am6
-rw-r--r--addressbook/util/Makefile.am8
-rw-r--r--art/Makefile.am12
-rw-r--r--art/confidential-stamp.jpgbin0 -> 5708 bytes
-rw-r--r--art/draft-paper.pngbin0 -> 597 bytes
-rw-r--r--art/draft-stamp.jpgbin0 -> 4660 bytes
-rw-r--r--art/midnight-stars.jpgbin0 -> 7190 bytes
-rw-r--r--art/paper.pngbin0 -> 266 bytes
-rw-r--r--art/rect.pngbin0 -> 3795 bytes
-rw-r--r--art/ribbon.jpgbin0 -> 3193 bytes
-rw-r--r--art/texture.pngbin0 -> 137 bytes
-rw-r--r--calendar/alarm-notify/Makefile.am8
-rw-r--r--calendar/gui/Makefile.am8
-rw-r--r--calendar/gui/dialogs/Makefile.am6
-rw-r--r--calendar/importers/Makefile.am6
-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
-rw-r--r--configure.ac58
-rw-r--r--data/evolution.convert1
-rw-r--r--data/org.gnome.evolution.mail.gschema.xml.in15
-rw-r--r--doc/reference/evolution-mail-composer/Makefile.am2
-rw-r--r--doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml1
-rw-r--r--doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt19
-rw-r--r--doc/reference/evolution-mail-composer/evolution-mail-composer.types2
-rw-r--r--doc/reference/evolution-mail-formatter/Makefile.am2
-rw-r--r--doc/reference/evolution-shell/Makefile.am2
-rw-r--r--doc/reference/evolution-util/Makefile.am5
-rw-r--r--doc/reference/evolution-util/evolution-util-docs.sgml30
-rw-r--r--doc/reference/evolution-util/evolution-util-sections.txt1081
-rw-r--r--doc/reference/evolution-util/evolution-util.types26
-rw-r--r--e-util/Makefile.am67
-rw-r--r--e-util/e-action-combo-box.c42
-rw-r--r--e-util/e-action-combo-box.h1
-rw-r--r--e-util/e-color-chooser-widget.c253
-rw-r--r--e-util/e-color-chooser-widget.h71
-rw-r--r--e-util/e-color-combo.c976
-rw-r--r--e-util/e-color-combo.h96
-rw-r--r--e-util/e-emoticon-action.c278
-rw-r--r--e-util/e-emoticon-action.h73
-rw-r--r--e-util/e-emoticon-chooser-menu.c184
-rw-r--r--e-util/e-emoticon-chooser-menu.h70
-rw-r--r--e-util/e-emoticon-chooser.c178
-rw-r--r--e-util/e-emoticon-chooser.h77
-rw-r--r--e-util/e-emoticon-tool-button.c695
-rw-r--r--e-util/e-emoticon-tool-button.h75
-rw-r--r--e-util/e-emoticon.c118
-rw-r--r--e-util/e-emoticon.h53
-rw-r--r--e-util/e-focus-tracker.c45
-rw-r--r--e-util/e-html-editor-actions.c2028
-rw-r--r--e-util/e-html-editor-actions.h155
-rw-r--r--e-util/e-html-editor-cell-dialog.c872
-rw-r--r--e-util/e-html-editor-cell-dialog.h72
-rw-r--r--e-util/e-html-editor-dialog.c248
-rw-r--r--e-util/e-html-editor-dialog.h74
-rw-r--r--e-util/e-html-editor-find-dialog.c224
-rw-r--r--e-util/e-html-editor-find-dialog.h73
-rw-r--r--e-util/e-html-editor-hrule-dialog.c421
-rw-r--r--e-util/e-html-editor-hrule-dialog.h70
-rw-r--r--e-util/e-html-editor-image-dialog.c703
-rw-r--r--e-util/e-html-editor-image-dialog.h73
-rw-r--r--e-util/e-html-editor-link-dialog.c390
-rw-r--r--e-util/e-html-editor-link-dialog.h70
-rw-r--r--e-util/e-html-editor-manager.ui181
-rw-r--r--e-util/e-html-editor-page-dialog.c513
-rw-r--r--e-util/e-html-editor-page-dialog.h70
-rw-r--r--e-util/e-html-editor-paragraph-dialog.c154
-rw-r--r--e-util/e-html-editor-paragraph-dialog.h71
-rw-r--r--e-util/e-html-editor-private.h103
-rw-r--r--e-util/e-html-editor-replace-dialog.c288
-rw-r--r--e-util/e-html-editor-replace-dialog.h71
-rw-r--r--e-util/e-html-editor-selection.c5576
-rw-r--r--e-util/e-html-editor-selection.h250
-rw-r--r--e-util/e-html-editor-spell-check-dialog.c710
-rw-r--r--e-util/e-html-editor-spell-check-dialog.h73
-rw-r--r--e-util/e-html-editor-table-dialog.c866
-rw-r--r--e-util/e-html-editor-table-dialog.h69
-rw-r--r--e-util/e-html-editor-text-dialog.c298
-rw-r--r--e-util/e-html-editor-text-dialog.h69
-rw-r--r--e-util/e-html-editor-utils.c116
-rw-r--r--e-util/e-html-editor-utils.h44
-rw-r--r--e-util/e-html-editor-view.c6303
-rw-r--r--e-util/e-html-editor-view.h164
-rw-r--r--e-util/e-html-editor.c1178
-rw-r--r--e-util/e-html-editor.h108
-rw-r--r--e-util/e-image-chooser-dialog.c223
-rw-r--r--e-util/e-image-chooser-dialog.h74
-rw-r--r--e-util/e-mail-signature-editor.c319
-rw-r--r--e-util/e-mail-signature-editor.h8
-rw-r--r--e-util/e-mail-signature-manager.c18
-rw-r--r--e-util/e-mail-signature-preview.c8
-rw-r--r--e-util/e-misc-utils.c44
-rw-r--r--e-util/e-misc-utils.h3
-rw-r--r--e-util/e-name-selector-entry.c6
-rw-r--r--e-util/e-spell-checker.c783
-rw-r--r--e-util/e-spell-checker.h95
-rw-r--r--e-util/e-spell-dictionary.c797
-rw-r--r--e-util/e-spell-dictionary.h99
-rw-r--r--e-util/e-spell-entry.c373
-rw-r--r--e-util/e-spell-entry.h7
-rw-r--r--e-util/e-util-enums.h221
-rw-r--r--e-util/e-util.h26
-rw-r--r--e-util/e-web-view-gtkhtml.c2352
-rw-r--r--e-util/e-web-view-gtkhtml.h209
-rw-r--r--e-util/e-web-view.c150
-rw-r--r--e-util/e-web-view.h13
-rw-r--r--e-util/test-html-editor.c497
-rw-r--r--em-format/Makefile.am8
-rw-r--r--em-format/e-mail-formatter-enums.h18
-rw-r--r--em-format/e-mail-formatter-quote-attachment.c9
-rw-r--r--em-format/e-mail-formatter-quote-text-html.c2
-rw-r--r--em-format/e-mail-formatter-quote-text-plain.c4
-rw-r--r--em-format/e-mail-formatter-quote.c54
-rw-r--r--em-format/e-mail-formatter.c10
-rw-r--r--em-format/e-mail-formatter.h4
-rw-r--r--libemail-engine/Makefile.am2
-rw-r--r--mail/Makefile.am5
-rw-r--r--mail/e-http-request.c72
-rw-r--r--mail/e-mail-display.c130
-rw-r--r--mail/e-mail-reader-utils.c1
-rw-r--r--mail/e-mail-reader.c2
-rw-r--r--mail/em-composer-utils.c120
-rw-r--r--mail/em-utils.c5
-rw-r--r--mail/em-utils.h1
-rw-r--r--mail/importers/Makefile.am6
-rw-r--r--mail/mail-config.ui93
-rw-r--r--modules/addressbook/Makefile.am8
-rw-r--r--modules/backup-restore/Makefile.am4
-rw-r--r--modules/backup-restore/evolution-backup-tool.c44
-rw-r--r--modules/book-config-google/Makefile.am6
-rw-r--r--modules/book-config-ldap/Makefile.am8
-rw-r--r--modules/book-config-local/Makefile.am6
-rw-r--r--modules/book-config-webdav/Makefile.am6
-rw-r--r--modules/cal-config-caldav/Makefile.am8
-rw-r--r--modules/cal-config-contacts/Makefile.am6
-rw-r--r--modules/cal-config-google/Makefile.am8
-rw-r--r--modules/cal-config-local/Makefile.am6
-rw-r--r--modules/cal-config-weather/Makefile.am8
-rw-r--r--modules/cal-config-webcal/Makefile.am6
-rw-r--r--modules/calendar/Makefile.am6
-rw-r--r--modules/composer-autosave/Makefile.am41
-rw-r--r--modules/composer-autosave/e-composer-autosave.c14
-rw-r--r--modules/contact-photos/Makefile.am2
-rw-r--r--modules/gravatar/Makefile.am2
-rw-r--r--modules/itip-formatter/Makefile.am6
-rw-r--r--modules/itip-formatter/itip-view.c16
-rw-r--r--modules/itip-formatter/plugin/Makefile.am6
-rw-r--r--modules/mail-config/Makefile.am2
-rw-r--r--modules/mail/Makefile.am6
-rw-r--r--modules/mail/e-mail-shell-backend.c19
-rw-r--r--modules/mail/e-mail-shell-view-private.c3
-rw-r--r--modules/mail/e-mail-shell-view-private.h1
-rw-r--r--modules/mail/em-composer-prefs.c118
-rw-r--r--modules/mail/em-composer-prefs.h5
-rw-r--r--modules/mail/em-mailer-prefs.c13
-rw-r--r--modules/mailto-handler/Makefile.am6
-rw-r--r--modules/mdn/Makefile.am6
-rw-r--r--modules/offline-alert/Makefile.am6
-rw-r--r--modules/plugin-lib/Makefile.am6
-rw-r--r--modules/plugin-manager/Makefile.am6
-rw-r--r--modules/prefer-plain/Makefile.am6
-rw-r--r--modules/prefer-plain/plugin/Makefile.am6
-rw-r--r--modules/settings/Makefile.am6
-rw-r--r--modules/settings/e-settings-deprecated.c2
-rw-r--r--modules/settings/e-settings-spell-checker.c117
-rw-r--r--modules/settings/e-settings-spell-checker.h65
-rw-r--r--modules/settings/e-settings-web-view-gtkhtml.c324
-rw-r--r--modules/settings/e-settings-web-view-gtkhtml.h64
-rw-r--r--modules/settings/e-settings-web-view.c189
-rw-r--r--modules/settings/evolution-module-settings.c4
-rw-r--r--modules/spamassassin/Makefile.am6
-rw-r--r--modules/startup-wizard/Makefile.am2
-rw-r--r--modules/text-highlight/Makefile.am6
-rw-r--r--modules/tnef-attachment/Makefile.am8
-rw-r--r--modules/vcard-inline/Makefile.am4
-rw-r--r--modules/web-inspector/Makefile.am6
-rw-r--r--plugins/attachment-reminder/Makefile.am6
-rw-r--r--plugins/bbdb/Makefile.am6
-rw-r--r--plugins/dbx-import/Makefile.am6
-rw-r--r--plugins/email-custom-header/Makefile.am6
-rw-r--r--plugins/email-custom-header/email-custom-header.c11
-rw-r--r--plugins/external-editor/Makefile.am6
-rw-r--r--plugins/external-editor/external-editor.c106
-rw-r--r--plugins/face/Makefile.am6
-rw-r--r--plugins/face/face.c12
-rw-r--r--plugins/mail-notification/Makefile.am6
-rw-r--r--plugins/mail-to-task/Makefile.am6
-rw-r--r--plugins/mailing-list-actions/Makefile.am6
-rw-r--r--plugins/pst-import/Makefile.am8
-rw-r--r--plugins/publish-calendar/Makefile.am8
-rw-r--r--plugins/save-calendar/Makefile.am6
-rw-r--r--plugins/templates/Makefile.am6
-rw-r--r--plugins/templates/templates.c6
-rw-r--r--po/POTFILES.in19
-rw-r--r--po/POTFILES.skip1
-rw-r--r--shell/Makefile.am8
-rw-r--r--smime/gui/Makefile.am8
-rw-r--r--smime/lib/Makefile.am8
211 files changed, 32916 insertions, 5470 deletions
diff --git a/addressbook/gui/contact-editor/Makefile.am b/addressbook/gui/contact-editor/Makefile.am
index e62b6cf058..8356a7e5a4 100644
--- a/addressbook/gui/contact-editor/Makefile.am
+++ b/addressbook/gui/contact-editor/Makefile.am
@@ -11,8 +11,8 @@ libecontacteditor_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"contact-editor\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libecontacteditor_la_SOURCES = \
eab-editor.c \
@@ -36,7 +36,7 @@ libecontacteditor_la_LIBADD = \
$(EVOLUTION_ADDRESSBOOK_LIBS) \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
ui_DATA = \
contact-editor.ui \
diff --git a/addressbook/gui/contact-list-editor/Makefile.am b/addressbook/gui/contact-list-editor/Makefile.am
index faf2d435f9..c1c2bd2604 100644
--- a/addressbook/gui/contact-list-editor/Makefile.am
+++ b/addressbook/gui/contact-list-editor/Makefile.am
@@ -11,8 +11,8 @@ libecontactlisteditor_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"contact-list-editor\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libecontactlisteditor_la_SOURCES = \
e-contact-list-editor.c \
@@ -29,7 +29,7 @@ libecontactlisteditor_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
ui_DATA = contact-list-editor.ui
diff --git a/addressbook/gui/widgets/Makefile.am b/addressbook/gui/widgets/Makefile.am
index 715f84b63c..26b8b0156e 100644
--- a/addressbook/gui/widgets/Makefile.am
+++ b/addressbook/gui/widgets/Makefile.am
@@ -18,10 +18,10 @@ libeabwidgets_la_CPPFLAGS = \
-I$(top_builddir)/shell \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CHAMPLAIN_CFLAGS) \
$(GEO_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
eabincludedir = $(privincludedir)/addressbook/gui/widgets
@@ -81,9 +81,9 @@ libeabwidgets_la_LIBADD = \
$(top_builddir)/addressbook/util/libeabutil.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(CHAMPLAIN_LIBS) \
- $(GEO_LIBS)
+ $(GEO_LIBS) \
+ $(NULL)
dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/addressbook/gui/widgets/eab-contact-formatter.c b/addressbook/gui/widgets/eab-contact-formatter.c
index 7bfa46893b..34b3a5ed3c 100644
--- a/addressbook/gui/widgets/eab-contact-formatter.c
+++ b/addressbook/gui/widgets/eab-contact-formatter.c
@@ -1395,13 +1395,7 @@ collapse_contacts_list (WebKitDOMEventTarget *event_target,
gboolean hidden;
document = user_data;
-#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */
- id = webkit_dom_element_get_id (
- WEBKIT_DOM_ELEMENT (event_target));
-#else
- id = webkit_dom_html_element_get_id (
- WEBKIT_DOM_HTML_ELEMENT (event_target));
-#endif
+ id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target));
list_id = g_strconcat ("list-", id, NULL);
list = webkit_dom_document_get_element_by_id (document, list_id);
diff --git a/addressbook/importers/Makefile.am b/addressbook/importers/Makefile.am
index 89f8f9bea9..0d1d2624b0 100644
--- a/addressbook/importers/Makefile.am
+++ b/addressbook/importers/Makefile.am
@@ -10,8 +10,8 @@ libevolution_addressbook_importers_la_CPPFLAGS = \
-I$(top_builddir)/addressbook \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_addressbook_importers_la_SOURCES = \
evolution-ldif-importer.c \
@@ -27,7 +27,7 @@ libevolution_addressbook_importers_la_LIBADD = \
$(top_builddir)/addressbook/util/libeabutil.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(IMPORTERS_LIBS)
+ $(IMPORTERS_LIBS) \
+ $(NULL)
-include $(top_srcdir)/git.mk
diff --git a/addressbook/printing/Makefile.am b/addressbook/printing/Makefile.am
index a6e3aac41c..c0773590bd 100644
--- a/addressbook/printing/Makefile.am
+++ b/addressbook/printing/Makefile.am
@@ -13,8 +13,8 @@ libecontactprint_la_CPPFLAGS = \
-DEVOLUTION_ECPSDIR=\""$(ecpsdir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
noinst_LTLIBRARIES = libecontactprint.la
@@ -30,7 +30,7 @@ libecontactprint_la_LIBADD = \
$(top_builddir)/addressbook/util/libeabutil.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = \
$(ecps_DATA)
diff --git a/addressbook/util/Makefile.am b/addressbook/util/Makefile.am
index 4657dbb486..33f1f1b29f 100644
--- a/addressbook/util/Makefile.am
+++ b/addressbook/util/Makefile.am
@@ -12,9 +12,9 @@ libeabutil_la_CPPFLAGS = \
-I$(top_srcdir)/shell \
$(CAMEL_CFLAGS) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libeabutil_la_SOURCES = \
eab-book-util.c \
@@ -27,8 +27,8 @@ libeabutil_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(CAMEL_LIBS) \
$(EVOLUTION_DATA_SERVER_LIBS) \
- $(GTKHTML_LIBS) \
- $(GNOME_PLATFORM_LIBS)
+ $(GNOME_PLATFORM_LIBS) \
+ $(NULL)
dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/art/Makefile.am b/art/Makefile.am
index 850be7e8ce..276d435b05 100644
--- a/art/Makefile.am
+++ b/art/Makefile.am
@@ -1,7 +1,15 @@
images_DATA = \
- world_map-960.png \
+ confidential-stamp.jpg \
+ draft-paper.png \
+ draft-stamp.jpg \
+ midnight-stars.jpg \
+ minus.png \
+ paper.png \
plus.png \
- minus.png
+ rect.png \
+ ribbon.jpg \
+ texture.png \
+ world_map-960.png
EXTRA_DIST = \
README \
diff --git a/art/confidential-stamp.jpg b/art/confidential-stamp.jpg
new file mode 100644
index 0000000000..0dece7c04f
--- /dev/null
+++ b/art/confidential-stamp.jpg
Binary files differ
diff --git a/art/draft-paper.png b/art/draft-paper.png
new file mode 100644
index 0000000000..177d568eab
--- /dev/null
+++ b/art/draft-paper.png
Binary files differ
diff --git a/art/draft-stamp.jpg b/art/draft-stamp.jpg
new file mode 100644
index 0000000000..623f50ea56
--- /dev/null
+++ b/art/draft-stamp.jpg
Binary files differ
diff --git a/art/midnight-stars.jpg b/art/midnight-stars.jpg
new file mode 100644
index 0000000000..22f01b36f8
--- /dev/null
+++ b/art/midnight-stars.jpg
Binary files differ
diff --git a/art/paper.png b/art/paper.png
new file mode 100644
index 0000000000..bca355b80d
--- /dev/null
+++ b/art/paper.png
Binary files differ
diff --git a/art/rect.png b/art/rect.png
new file mode 100644
index 0000000000..b7e633c22b
--- /dev/null
+++ b/art/rect.png
Binary files differ
diff --git a/art/ribbon.jpg b/art/ribbon.jpg
new file mode 100644
index 0000000000..03ca65e8c3
--- /dev/null
+++ b/art/ribbon.jpg
Binary files differ
diff --git a/art/texture.png b/art/texture.png
new file mode 100644
index 0000000000..b0925a6cdc
--- /dev/null
+++ b/art/texture.png
Binary files differ
diff --git a/calendar/alarm-notify/Makefile.am b/calendar/alarm-notify/Makefile.am
index 057ac6a9a4..ecd24afa43 100644
--- a/calendar/alarm-notify/Makefile.am
+++ b/calendar/alarm-notify/Makefile.am
@@ -21,8 +21,8 @@ evolution_alarm_notify_CPPFLAGS = \
$(GNOME_PLATFORM_CFLAGS) \
$(LIBNOTIFY_CFLAGS) \
$(CANBERRA_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
ui_DATA = \
alarm-notify.ui
@@ -55,8 +55,8 @@ evolution_alarm_notify_LDADD = \
$(GNOME_PLATFORM_LIBS) \
$(LIBNOTIFY_LIBS) \
$(CANBERRA_LIBS) \
- $(GTKHTML_LIBS)
- $(EVOLUTIONALARMNOTIFYICON)
+ $(EVOLUTIONALARMNOTIFYICON) \
+ $(NULL)
evolution_alarm_notify_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index de752c11d4..2dca7d50bd 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -67,9 +67,9 @@ libevolution_calendar_la_CPPFLAGS = \
-DPREFIX=\""$(prefix)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBSOUP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
etspec_DATA = \
e-calendar-table.etspec \
@@ -207,8 +207,8 @@ libevolution_calendar_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(LIBSOUP_LIBS)
+ $(LIBSOUP_LIBS) \
+ $(NULL)
libevolution_calendar_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am
index 9c7b3053a2..ec5afaa7a2 100644
--- a/calendar/gui/dialogs/Makefile.am
+++ b/calendar/gui/dialogs/Makefile.am
@@ -14,8 +14,8 @@ libcal_dialogs_la_CPPFLAGS = \
-DPREFIX=\""$(prefix)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
ecalendarincludedir = $(privincludedir)/calendar/gui/dialogs
@@ -52,7 +52,7 @@ libcal_dialogs_la_LIBADD = \
$(top_builddir)/addressbook/util/libeabutil.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
libcal_dialogs_la_SOURCES = \
alarm-dialog.c \
diff --git a/calendar/importers/Makefile.am b/calendar/importers/Makefile.am
index 206a852f4c..957de01280 100644
--- a/calendar/importers/Makefile.am
+++ b/calendar/importers/Makefile.am
@@ -9,8 +9,8 @@ libevolution_calendar_importers_la_CPPFLAGS = \
-I$(top_builddir)/calendar \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_calendar_importers_la_SOURCES = \
evolution-calendar-importer.h \
@@ -23,6 +23,6 @@ libevolution_calendar_importers_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
-include $(top_srcdir)/git.mk
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 */
diff --git a/configure.ac b/configure.ac
index b03c1e6215..fcd15e5944 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,9 +48,10 @@ m4_define([eds_minimum_version], [evo_version])
m4_define([gtkhtml_minimum_version], [4.5.2])
m4_define([gdk_pixbuf_minimum_version], [2.24.0])
m4_define([gcr_minimum_version], [3.4])
+m4_define([enchant_minimum_version], [1.1.7])
m4_define([gnome_desktop_minimum_version], [2.91.3])
m4_define([gsettings_desktop_schemas_minimum_version], [2.91.92])
-m4_define([webkitgtk_minimum_version], [2.0.1])
+m4_define([webkitgtk_minimum_version], [2.2.0])
m4_define([libgdata_minimum_version], [0.10])
m4_define([libxml_minimum_version], [2.7.3])
m4_define([shared_mime_info_minimum_version], [0.22])
@@ -286,6 +287,15 @@ PKG_CHECK_MODULES([EVOLUTION_DATA_SERVER],
AC_SUBST(EVOLUTION_DATA_SERVER_CFLAGS)
AC_SUBST(EVOLUTION_DATA_SERVER_LIBS)
+
+dnl ****************
+dnl Enchant Library
+dnl ****************
+PKG_CHECK_MODULES([ENCHANT],
+ [enchant >= enchant_minimum_version])
+AC_SUBST(ENCHANT_CFLAGS)
+AC_SUBST(ENCHANT_LIBS)
+
dnl ******************************
dnl Canberra / Canberra-GTK Sound
dnl ******************************
@@ -482,6 +492,42 @@ fi
AC_CHECK_FUNCS(mkdtemp)
dnl **************************************************
+dnl iso-codes
+dnl **************************************************
+AC_MSG_CHECKING([for iso-codes package])
+have_iso_codes=no
+if $PKG_CONFIG --exists iso-codes; then
+ if $PKG_CONFIG iso-codes --atleast-version=0.49; then
+ have_iso_codes=yes
+ AC_MSG_RESULT([$have_iso_codes])
+ else
+ AC_MSG_WARN([iso-codes detected, but version 0.49 or later is required due to licensing])
+ fi
+else
+ AC_MSG_RESULT([$have_iso_codes])
+fi
+
+if test "x$have_iso_codes" = "xyes"; then
+ AC_MSG_CHECKING([whether iso-codes has iso-639 and iso-3166 domains])
+ if $PKG_CONFIG --variable=domains iso-codes | grep 639 >/dev/null 2>&1 && \
+ $PKG_CONFIG --variable=domains iso-codes | grep 3166 >/dev/null 2>&1 ; then
+ result=yes
+ else
+ result=no
+ have_iso_codes=no
+ fi
+ AC_MSG_RESULT([$result])
+fi
+
+if test "x$have_iso_codes" = "xyes"; then
+ AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX],
+ ["`$PKG_CONFIG --variable=prefix iso-codes`"],
+ [ISO codes prefix])
+ AC_DEFINE([HAVE_ISO_CODES], [1],
+ [Define if you have the iso-codes package])
+fi
+
+dnl **************************************************
dnl Accessibility support
dnl **************************************************
PKG_CHECK_MODULES([A11Y], [atk])
@@ -567,16 +613,6 @@ AM_CONDITIONAL(SUNLDAP, [test x$with_sunldap != xno])
AC_DEFINE(HANDLE_LIBICAL_MEMORY, 1, [Define it once memory returned by libical is free'ed properly])
-dnl *************************
-dnl GTKHTML check
-dnl XXX Drop the version from the package name?
-dnl *************************
-PKG_CHECK_MODULES([GTKHTML],
- [libgtkhtml-4.0 >= gtkhtml_minimum_version
- gtkhtml-editor-4.0])
-AC_SUBST(GTKHTML_CFLAGS)
-AC_SUBST(GTKHTML_LIBS)
-
dnl ********************************************************************************
dnl security extension support (SSL and S/MIME)
dnl
diff --git a/data/evolution.convert b/data/evolution.convert
index 60ff3bbc4b..d25e6f48ea 100644
--- a/data/evolution.convert
+++ b/data/evolution.convert
@@ -128,7 +128,6 @@ composer-show-post-from = /apps/evolution/mail/composer/show_post_from
composer-show-post-reply-to = /apps/evolution/mail/composer/show_post_reply_to
composer-show-reply-to = /apps/evolution/mail/composer/show_mail_reply_to
composer-sign-reply-if-signed = /apps/evolution/mail/composer/sign_reply_if_signed
-composer-spell-color = /apps/evolution/mail/composer/spell_color
composer-spell-languages = /apps/evolution/mail/composer/spell_languages
composer-top-signature = /apps/evolution/mail/composer/top_signature
default-account = /apps/evolution/mail/default_account
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index c1b2e25545..c13540db63 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -95,11 +95,6 @@
<_summary>Send HTML mail by default</_summary>
<_description>Send HTML mail by default.</_description>
</key>
- <key name="composer-spell-color" type="s">
- <default>'#FFFF00000000'</default>
- <_summary>Spell checking color</_summary>
- <_description>Underline color for misspelled words when using inline spelling.</_description>
- </key>
<key name="composer-spell-languages" type="as">
<default>[]</default>
<_summary>Spell checking languages</_summary>
@@ -165,6 +160,16 @@
<_summary>List of localized 'Re'</_summary>
<_description>Comma-separated list of localized 'Re' abbreviations to skip in a subject text when replying to a message, as an addition to the standard "Re" prefix. An example is 'SV,AV'.</_description>
</key>
+ <key name="composer-developer-mode" type="b">
+ <default>false</default>
+ <_summary>Enable developer mode</_summary>
+ <_description>Enables some hidden actions and tools aimed for development and debugging.</_description>
+ </key>
+ <key name="composer-word-wrap-length" type="i">
+ <default>71</default>
+ <_summary>Number of characters for wrapping</_summary>
+ <_description>Will autowrap lines after given number of characters.</_description>
+ </key>
<key name="drag-and-drop-save-file-format" type="s">
<default>'mbox'</default>
<_summary>Save file format for drag-and-drop operation</_summary>
diff --git a/doc/reference/evolution-mail-composer/Makefile.am b/doc/reference/evolution-mail-composer/Makefile.am
index 580dc63a3b..3fbc28fe7b 100644
--- a/doc/reference/evolution-mail-composer/Makefile.am
+++ b/doc/reference/evolution-mail-composer/Makefile.am
@@ -22,14 +22,12 @@ GTKDOC_CFLAGS= \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(NULL)
GTKDOC_LIBS= \
$(top_builddir)/composer/libevolution-mail-composer.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
# This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml b/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
index becbfd678f..b7d06b39db 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
@@ -16,7 +16,6 @@
<chapter>
<title>Mail Composition</title>
<xi:include href="xml/e-msg-composer.xml"/>
- <xi:include href="xml/e-composer-activity.xml"/>
<xi:include href="xml/e-composer-header-table.xml"/>
<xi:include href="xml/e-composer-header.xml"/>
<xi:include href="xml/e-composer-from-header.xml"/>
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt b/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
index 4b286f94ec..cbe3f9ec3e 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
@@ -1,23 +1,4 @@
<SECTION>
-<FILE>e-composer-activity</FILE>
-<TITLE>EComposerActivity</TITLE>
-EComposerActivity
-e_composer_activity_new
-e_composer_activity_get_composer
-<SUBSECTION Standard>
-E_COMPOSER_ACTIVITY
-E_IS_COMPOSER_ACTIVITY
-E_TYPE_COMPOSER_ACTIVITY
-E_COMPOSER_ACTIVITY_CLASS
-E_IS_COMPOSER_ACTIVITY_CLASS
-E_COMPOSER_ACTIVITY_GET_CLASS
-EComposerActivityClass
-e_composer_activity_get_type
-<SUBSECTION Private>
-EComposerActivityPrivate
-</SECTION>
-
-<SECTION>
<FILE>e-composer-from-header</FILE>
<TITLE>EComposerFromHeader</TITLE>
EComposerFromHeader
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer.types b/doc/reference/evolution-mail-composer/evolution-mail-composer.types
index bf4ab7c4d6..5e26731470 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer.types
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer.types
@@ -1,4 +1,3 @@
-#include <composer/e-composer-activity.h>
#include <composer/e-composer-from-header.h>
#include <composer/e-composer-header-table.h>
#include <composer/e-composer-header.h>
@@ -8,7 +7,6 @@
#include <composer/e-composer-text-header.h>
#include <composer/e-msg-composer.h>
-e_composer_activity_get_type
e_composer_from_header_get_type
e_composer_header_get_type
e_composer_header_table_get_type
diff --git a/doc/reference/evolution-mail-formatter/Makefile.am b/doc/reference/evolution-mail-formatter/Makefile.am
index aff3eb7142..ed313fcb86 100644
--- a/doc/reference/evolution-mail-formatter/Makefile.am
+++ b/doc/reference/evolution-mail-formatter/Makefile.am
@@ -21,14 +21,12 @@ GTKDOC_CFLAGS= \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(NULL)
GTKDOC_LIBS= \
$(top_builddir)/em-format/libevolution-mail-formatter.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
# This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-shell/Makefile.am b/doc/reference/evolution-shell/Makefile.am
index 7afbc72919..c64068e384 100644
--- a/doc/reference/evolution-shell/Makefile.am
+++ b/doc/reference/evolution-shell/Makefile.am
@@ -22,7 +22,6 @@ GTKDOC_CFLAGS= \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(NULL)
GTKDOC_LIBS= \
@@ -31,7 +30,6 @@ GTKDOC_LIBS= \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
# This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-util/Makefile.am b/doc/reference/evolution-util/Makefile.am
index a7055a4f39..48d1d13845 100644
--- a/doc/reference/evolution-util/Makefile.am
+++ b/doc/reference/evolution-util/Makefile.am
@@ -16,6 +16,9 @@ CFILE_GLOB = $(top_srcdir)/e-util/*.c
# Ignore all accessiblity headers.
IGNORE_HFILES = \
+ e-html-editor-actions.h \
+ e-html-editor-private.h \
+ e-html-editor-utils.h \
e-marshal.h \
e-table-col-dnd.h \
e-table-defines.h \
@@ -52,14 +55,12 @@ GTKDOC_CFLAGS = \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(NULL)
GTKDOC_LIBS = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
# Extra options to supply to gtkdoc-mkdb
diff --git a/doc/reference/evolution-util/evolution-util-docs.sgml b/doc/reference/evolution-util/evolution-util-docs.sgml
index 53badcc278..262ddae97d 100644
--- a/doc/reference/evolution-util/evolution-util-docs.sgml
+++ b/doc/reference/evolution-util/evolution-util-docs.sgml
@@ -94,12 +94,40 @@
<title>HTML Rendering</title>
<xi:include href="xml/e-web-view.xml"/>
<xi:include href="xml/e-web-view-preview.xml"/>
- <xi:include href="xml/e-web-view-gtkhtml.xml"/>
<xi:include href="xml/e-file-request.xml"/>
<xi:include href="xml/e-stock-request.xml"/>
</chapter>
<chapter>
+ <title>HTML Editing</title>
+ <xi:include href="xml/e-color-chooser-widget.xml"/>
+ <xi:include href="xml/e-color-combo.xml"/>
+ <xi:include href="xml/e-emoticon.xml"/>
+ <xi:include href="xml/e-emoticon-action.xml"/>
+ <xi:include href="xml/e-emoticon-chooser.xml"/>
+ <xi:include href="xml/e-emoticon-chooser-menu.xml"/>
+ <xi:include href="xml/e-emoticon-tool-button.xml"/>
+ <xi:include href="xml/e-html-editor.xml"/>
+ <xi:include href="xml/e-html-editor-cell-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-find-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-hrule-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-image-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-link-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-page-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-paragraph-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-replace-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-selection.xml"/>
+ <xi:include href="xml/e-html-editor-spell-check-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-table-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-text-dialog.xml"/>
+ <xi:include href="xml/e-html-editor-widget.xml"/>
+ <xi:include href="xml/e-image-chooser-dialog.xml"/>
+ <xi:include href="xml/e-spell-checker.xml"/>
+ <xi:include href="xml/e-spell-dictionary.xml"/>
+ </chapter>
+
+ <chapter>
<title>Mail Signatures</title>
<xi:include href="xml/e-mail-signature-combo-box.xml"/>
<xi:include href="xml/e-mail-signature-editor.xml"/>
diff --git a/doc/reference/evolution-util/evolution-util-sections.txt b/doc/reference/evolution-util/evolution-util-sections.txt
index 5f7497c5f9..b5b4ee76bb 100644
--- a/doc/reference/evolution-util/evolution-util-sections.txt
+++ b/doc/reference/evolution-util/evolution-util-sections.txt
@@ -1323,6 +1323,55 @@ EClientSelectorPrivate
</SECTION>
<SECTION>
+<FILE>e-color-chooser-widget</FILE>
+<TITLE>EColorChooserWidget</TITLE>
+EColorChooserWidget
+e_color_chooser_widget_new
+<SUBSECTION Standard>
+E_COLOR_CHOOSER_WIDGET
+E_IS_COLOR_CHOOSER_WIDGET
+E_TYPE_COLOR_CHOOSER_WIDGET
+E_COLOR_CHOOSER_WIDGET_CLASS
+E_IS_COLOR_CHOOSER_WIDGET_CLASS
+E_COLOR_CHOOSER_WIDGET_GET_CLASS
+EColorChooserWidgetClass
+e_color_chooser_widget_get_type
+<SUBSECTION Private>
+EColorChooserWidgetPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-color-combo</FILE>
+<TITLE>EColorCombo</TITLE>
+EColorCombo
+e_color_combo_new
+e_color_combo_new_defaults
+e_color_combo_popup
+e_color_combo_popdown
+e_color_combo_get_current_color
+e_color_combo_set_current_color
+e_color_combo_get_default_color
+e_color_combo_set_default_color
+e_color_combo_get_default_label
+e_color_combo_set_default_label
+e_color_combo_get_default_transparent
+e_color_combo_set_default_transparent
+e_color_combo_get_palette
+e_color_combo_set_palette
+<SUBSECTION Standard>
+E_COLOR_COMBO
+E_IS_COLOR_COMBO
+E_TYPE_COLOR_COMBO
+E_COLOR_COMBO_CLASS
+E_IS_COLOR_COMBO_CLASS
+E_COLOR_COMBO_GET_CLASS
+EColorComboClass
+e_color_combo_get_type
+<SUBSECTION Private>
+EColorComboPrivate
+</SECTION>
+
+<SECTION>
<FILE>e-config</FILE>
<TITLE>EConfig</TITLE>
EConfig
@@ -1519,6 +1568,890 @@ e_dialog_button_new_with_icon
</SECTION>
<SECTION>
+<FILE>e-editor</FILE>
+<TITLE>EEditor</TITLE>
+EEditor
+e_editor_new
+e_editor_is_busy
+e_editor_get_editor_widget
+e_editor_get_builder
+e_editor_get_ui_manager
+e_editor_get_action
+e_editor_get_action_group
+e_editor_get_widget
+e_editor_get_managed_widget
+e_editor_get_filename
+e_editor_set_filename
+e_editor_new_activity
+e_editor_pack_above
+e_editor_save
+<SUBSECTION Standard>
+E_EDITOR
+E_IS_EDITOR
+E_TYPE_EDITOR
+E_EDITOR_CLASS
+E_IS_EDITOR_CLASS
+E_EDITOR_GET_CLASS
+EEditorClass
+e_editor_get_type
+<SUBSECTION Private>
+EEditorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-cell-dialog</FILE>
+<TITLE>EHTMLEditorCellDialog</TITLE>
+EHTMLEditorCellDialog
+e_html_editor_cell_dialog_new
+e_html_editor_cell_dialog_show
+<SUBSECTION Standard>
+E_HTML_EDITOR_CELL_DIALOG
+E_IS_HTML_EDITOR_CELL_DIALOG
+E_TYPE_HTML_EDITOR_CELL_DIALOG
+E_HTML_EDITOR_CELL_DIALOG_CLASS
+E_IS_HTML_EDITOR_CELL_DIALOG_CLASS
+E_HTML_EDITOR_CELL_DIALOG_GET_CLASS
+EHTMLEditorCellDialogClass
+e_html_editor_cell_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorCellDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-dialog</FILE>
+<TITLE>EEditorDialog</TITLE>
+e_editor_dialog_get_editor
+e_editor_dialog_get_button_box
+e_editor_dialog_get_container
+<SUBSECTION Standard>
+E_EDITOR_DIALOG
+E_IS_EDITOR_DIALOG
+E_TYPE_EDITOR_DIALOG
+E_EDITOR_DIALOG_CLASS
+E_IS_EDITOR_DIALOG_CLASS
+E_EDITOR_DIALOG_GET_CLASS
+EEditorDialogClass
+e_editor_dialog_get_type
+<SUBSECTION Private>
+EEditorDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-find-dialog</FILE>
+<TITLE>EHTMLEditorFindDialog</TITLE>
+e_html_editor_find_dialog_new
+e_html_editor_find_dialog_find_next
+<SUBSECTION Standard>
+E_HTML_EDITOR_FIND_DIALOG
+E_IS_HTML_EDITOR_FIND_DIALOG
+E_TYPE_HTML_EDITOR_FIND_DIALOG
+E_HTML_EDITOR_FIND_DIALOG_CLASS
+E_IS_HTML_EDITOR_FIND_DIALOG_CLASS
+E_HTML_EDITOR_FIND_DIALOG_GET_CLASS
+EHTMLEditorFindDialogClass
+e_html_editor_find_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorFindDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-hrule-dialog</FILE>
+<TITLE>EHTMLEditorHRuleDialog</TITLE>
+EHTMLEditorHRuleDialog
+e_html_editor_hrule_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_HRULE_DIALOG
+E_IS_HTML_EDITOR_HRULE_DIALOG
+E_TYPE_HTML_EDITOR_HRULE_DIALOG
+E_HTML_EDITOR_HRULE_DIALOG_CLASS
+E_IS_HTML_EDITOR_HRULE_DIALOG_CLASS
+E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS
+EHTMLEditorHRuleDialogClass
+e_html_editor_hrule_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorHRuleDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-image-dialog</FILE>
+<TITLE>EHTMLEditorImageDialog</TITLE>
+EHTMLEditorImageDialog
+e_html_editor_image_dialog_new
+e_html_editor_image_dialog_show
+<SUBSECTION Standard>
+E_HTML_EDITOR_IMAGE_DIALOG
+E_IS_HTML_EDITOR_IMAGE_DIALOG
+E_TYPE_HTML_EDITOR_IMAGE_DIALOG
+E_HTML_EDITOR_IMAGE_DIALOG_CLASS
+E_IS_HTML_EDITOR_IMAGE_DIALOG_CLASS
+E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS
+EHTMLEditorImageDialogClass
+e_html_editor_image_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorImageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-link-dialog</FILE>
+<TITLE>EHTMLEditorLinkDialog</TITLE>
+EHTMLEditorLinkDialog
+e_html_editor_link_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_LINK_DIALOG
+E_IS_HTML_EDITOR_LINK_DIALOG
+E_TYPE_HTML_EDITOR_LINK_DIALOG
+E_HTML_EDITOR_LINK_DIALOG_CLASS
+E_IS_HTML_EDITOR_LINK_DIALOG_CLASS
+E_HTML_EDITOR_LINK_DIALOG_GET_CLASS
+EHTMLEditorLinkDialogClass
+e_html_editor_link_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorLinkDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-page-dialog</FILE>
+<TITLE>EHTMLEditorPageDialog</TITLE>
+EHTMLEditorPageDialog
+e_html_editor_page_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_PAGE_DIALOG
+E_IS_HTML_EDITOR_PAGE_DIALOG
+E_TYPE_HTML_EDITOR_PAGE_DIALOG
+E_HTML_EDITOR_PAGE_DIALOG_CLASS
+E_IS_HTML_EDITOR_PAGE_DIALOG_CLASS
+E_HTML_EDITOR_PAGE_DIALOG_GET_CLASS
+EHTMLEditorPageDialogClass
+e_html_editor_page_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorPageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-paragraph-dialog</FILE>
+<TITLE>EHTMLEditorParagraphDialog</TITLE>
+EHTMLEditorParagraphDialog
+e_html_editor_paragraph_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_PARAGRAPH_DIALOG
+E_IS_HTML_EDITOR_PARAGRAPH_DIALOG
+E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG
+E_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_IS_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_CLASS
+EHTMLEditorParagraphDialogClass
+e_html_editor_paragraph_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorParagraphDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-replace-dialog</FILE>
+<TITLE>EHTMLEditorReplaceDialog</TITLE>
+EHTMLEditorReplaceDialog
+e_html_editor_replace_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_REPLACE_DIALOG
+E_IS_HTML_EDITOR_REPLACE_DIALOG
+E_TYPE_HTML_EDITOR_REPLACE_DIALOG
+E_HTML_EDITOR_REPLACE_DIALOG_CLASS
+E_IS_HTML_EDITOR_REPLACE_DIALOG_CLASS
+E_HTML_EDITOR_REPLACE_DIALOG_GET_CLASS
+EHTMLEditorReplaceDialogClass
+e_html_editor_replace_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorReplaceDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-selection</FILE>
+<TITLE>EEditorSelection</TITLE>
+e_editor_selection_ref_editor_widget
+e_editor_selection_has_text
+e_editor_selection_get_caret_word
+e_editor_selection_replace_caret_word
+EEditorSelectionAlignment
+e_editor_selection_get_alignment
+e_editor_selection_set_alignment
+e_editor_selection_get_background_color
+e_editor_selection_set_background_color
+e_editor_selection_get_font_color
+e_editor_selection_set_font_color
+e_editor_selection_get_font_name
+e_editor_selection_set_font_name
+EEditorSelectionFontSize
+e_editor_selection_get_font_size
+e_editor_selection_set_font_size
+EEditorSelectionBlockFormat
+e_editor_selection_get_block_format
+e_editor_selection_set_block_format
+e_editor_selection_is_citation
+e_editor_selection_is_indented
+e_editor_selection_indent
+e_editor_selection_unindent
+e_editor_selection_is_bold
+e_editor_selection_set_bold
+e_editor_selection_is_italic
+e_editor_selection_set_italic
+e_editor_selection_is_monospaced
+e_editor_selection_set_monospaced
+e_editor_selection_is_strike_through
+e_editor_selection_set_strike_through
+e_editor_selection_is_superscript
+e_editor_selection_set_superscript
+e_editor_selection_is_subscript
+e_editor_selection_set_subscript
+e_editor_selection_is_underline
+e_editor_selection_set_underline
+e_editor_selection_unlink
+e_editor_selection_create_link
+e_editor_selection_get_string
+e_editor_selection_replace
+e_editor_selection_insert_html
+e_editor_selection_insert_image
+e_editor_selection_insert_text
+e_editor_selection_wrap_lines
+e_editor_selection_save
+e_editor_selection_restore
+EEditorSelectionGranularity
+e_editor_selection_move
+e_editor_selection_extend
+<SUBSECTION Standard>
+E_EDITOR_SELECTION
+E_IS_EDITOR_SELECTION
+E_TYPE_EDITOR_SELECTION
+E_EDITOR_SELECTION_CLASS
+E_IS_EDITOR_SELECTION_CLASS
+E_EDITOR_SELECTION_GET_CLASS
+EEditorSelectionClass
+e_editor_selection_get_type
+<SUBSECTION Private>
+EEditorSelectionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-spell-check-dialog</FILE>
+<TITLE>EHTMLEditorSpellCheckDialog</TITLE>
+EHTMLEditorSpellCheckDialog
+e_html_editor_spell_check_dialog_new
+e_html_editor_spell_check_dialog_update_dictionaries
+<SUBSECTION Standard>
+E_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS
+EHTMLEditorSpellCheckDialogClass
+e_html_editor_spell_check_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorSpellCheckDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-table-dialog</FILE>
+<TITLE>EHTMLEditorTableDialog</TITLE>
+EHTMLEditorTableDialog
+e_html_editor_table_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_TABLE_DIALOG
+E_IS_HTML_EDITOR_TABLE_DIALOG
+E_TYPE_HTML_EDITOR_TABLE_DIALOG
+E_HTML_EDITOR_TABLE_DIALOG_CLASS
+E_IS_HTML_EDITOR_TABLE_DIALOG_CLASS
+E_HTML_EDITOR_TABLE_DIALOG_GET_CLASS
+EHTMLEditorTableDialogClass
+e_html_editor_table_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorTableDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-text-dialog</FILE>
+<TITLE>EHTMLEditorTextDialog</TITLE>
+EHTMLEditorTextDialog
+e_html_editor_text_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_TEXT_DIALOG
+E_IS_HTML_EDITOR_TEXT_DIALOG
+E_TYPE_HTML_EDITOR_TEXT_DIALOG
+E_HTML_EDITOR_TEXT_DIALOG_CLASS
+E_IS_HTML_EDITOR_TEXT_DIALOG_CLASS
+E_HTML_EDITOR_TEXT_DIALOG_GET_CLASS
+EHTMLEditorTextDialogClass
+e_html_editor_text_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorTextDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-widget</FILE>
+<TITLE>EEditorWidget</TITLE>
+EEditorWidget
+e_editor_widget_new
+e_editor_widget_get_selection
+EEditorWidgetCommand
+e_editor_widget_exec_command
+e_editor_widget_get_changed
+e_editor_widget_set_changed
+e_editor_widget_get_html_mode
+e_editor_widget_set_html_mode
+e_editor_widget_get_inline_spelling
+e_editor_widget_set_inline_spelling
+e_editor_widget_get_magic_links
+e_editor_widget_set_magic_links
+e_editor_widget_get_magic_smileys
+e_editor_widget_set_magic_smileys
+e_editor_widget_get_spell_checker
+e_editor_widget_get_text_html
+e_editor_widget_get_text_plain
+e_editor_widget_set_text_html
+e_editor_widget_set_text_plain
+e_editor_widget_paste_clipboard_quoted
+e_editor_widget_update_fonts
+<SUBSECTION Standard>
+E_EDITOR_WIDGET
+E_IS_EDITOR_WIDGET
+E_TYPE_EDITOR_WIDGET
+E_EDITOR_WIDGET_CLASS
+E_IS_EDITOR_WIDGET_CLASS
+E_EDITOR_WIDGET_GET_CLASS
+EEditorWidgetClass
+e_editor_widget_get_type
+<SUBSECTION Private>
+EEditorWidgetPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon</FILE>
+<TITLE>EEmoticon</TITLE>
+EEmoticon
+e_emoticon_equal
+e_emoticon_copy
+e_emoticon_free
+e_emoticon_get_uri
+<SUBSECTION Standard>
+E_TYPE_EMOTICON
+e_emoticon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-action</FILE>
+<TITLE>EEmoticonAction</TITLE>
+EEmoticonAction
+e_emoticon_action_new
+<SUBSECTION Standard>
+E_EMOTICON_ACTION
+E_IS_EMOTICON_ACTION
+E_TYPE_EMOTICON_ACTION
+E_EMOTICON_ACTION_CLASS
+E_IS_EMOTICON_ACTION_CLASS
+E_EMOTICON_ACTION_GET_CLASS
+EEmoticonActionClass
+e_emoticon_action_get_type
+<SUBSECTION Private>
+EEmoticonActionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser</FILE>
+<TITLE>EEmoticonChooser</TITLE>
+EEmoticonChooser
+EEmoticonChooserInterface
+e_emoticon_chooser_get_current_emoticon
+e_emoticon_chooser_set_current_emoticon
+e_emoticon_chooser_item_activated
+e_emoticon_chooser_get_items
+e_emoticon_chooser_lookup_emoticon
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER
+E_IS_EMOTICON_CHOOSER
+E_TYPE_EMOTICON_CHOOSER
+E_EMOTICON_CHOOSER_GET_INTERFACE
+e_emoticon_chooser_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser-menu</FILE>
+<TITLE>EEmoticonChooserMenu</TITLE>
+EEmoticonChooserMenu
+e_emoticon_chooser_menu_new
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER_MENU
+E_IS_EMOTICON_CHOOSER_MENU
+E_TYPE_EMOTICON_CHOOSER_MENU
+E_EMOTICON_CHOOSER_MENU_CLASS
+E_IS_EMOTICON_CHOOSER_MENU_CLASS
+E_EMOTICON_CHOOSER_MENU_GET_CLASS
+EEmoticonChooserMenuClass
+e_emoticon_chooser_menu_get_type
+<SUBSECTION Private>
+EEmoticonChooserMenuPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-tool-button</FILE>
+<TITLE>EEmoticonToolButton</TITLE>
+EEmoticonToolButton
+e_emoticon_tool_button_new
+e_emoticon_tool_button_popup
+e_emoticon_tool_button_popdown
+<SUBSECTION Standard>
+E_EMOTICON_TOOL_BUTTON
+E_IS_EMOTICON_TOOL_BUTTON
+E_TYPE_EMOTICON_TOOL_BUTTON
+E_EMOTICON_TOOL_BUTTON_CLASS
+E_IS_EMOTICON_TOOL_BUTTON_CLASS
+E_EMOTICON_TOOL_BUTTON_GET_CLASS
+EEmoticonToolButtonClass
+e_emoticon_tool_button_get_type
+<SUBSECTION Private>
+EEmoticonToolButtonPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor</FILE>
+<TITLE>EHTMLEditor</TITLE>
+EHTMLEditor
+e_html_editor_new
+e_html_editor_is_busy
+e_html_editor_get_view
+e_html_editor_get_builder
+e_html_editor_get_ui_manager
+e_html_editor_get_action
+e_html_editor_get_action_group
+e_html_editor_get_widget
+e_html_editor_get_managed_widget
+e_html_editor_get_filename
+e_html_editor_set_filename
+e_html_editor_new_activity
+e_html_editor_pack_above
+e_html_editor_save
+<SUBSECTION Standard>
+E_HTML_EDITOR
+E_IS_HTML_EDITOR
+E_TYPE_HTML_EDITOR
+E_HTML_EDITOR_CLASS
+E_IS_HTML_EDITOR_CLASS
+E_HTML_EDITOR_GET_CLASS
+EHTMLEditorClass
+e_html_editor_get_type
+<SUBSECTION Private>
+EHTMLEditorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-cell-dialog</FILE>
+<TITLE>EEditorCellDialog</TITLE>
+EEditorCellDialog
+e_editor_cell_dialog_new
+e_editor_cell_dialog_show
+<SUBSECTION Standard>
+E_EDITOR_CELL_DIALOG
+E_IS_EDITOR_CELL_DIALOG
+E_TYPE_EDITOR_CELL_DIALOG
+E_EDITOR_CELL_DIALOG_CLASS
+E_IS_EDITOR_CELL_DIALOG_CLASS
+E_EDITOR_CELL_DIALOG_GET_CLASS
+EEditorCellDialogClass
+e_editor_cell_dialog_get_type
+<SUBSECTION Private>
+EEditorCellDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-dialog</FILE>
+<TITLE>EHTMLEditorDialog</TITLE>
+e_html_editor_dialog_get_editor
+e_html_editor_dialog_get_button_box
+e_html_editor_dialog_get_container
+<SUBSECTION Standard>
+E_HTML_EDITOR_DIALOG
+E_IS_HTML_EDITOR_DIALOG
+E_TYPE_HTML_EDITOR_DIALOG
+E_HTML_EDITOR_DIALOG_CLASS
+E_IS_HTML_EDITOR_DIALOG_CLASS
+E_HTML_EDITOR_DIALOG_GET_CLASS
+EEditorDialogClass
+e_html_editor_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-find-dialog</FILE>
+<TITLE>EEditorFindDialog</TITLE>
+e_editor_find_dialog_new
+e_editor_find_dialog_find_next
+<SUBSECTION Standard>
+E_EDITOR_FIND_DIALOG
+E_IS_EDITOR_FIND_DIALOG
+E_TYPE_EDITOR_FIND_DIALOG
+E_EDITOR_FIND_DIALOG_CLASS
+E_IS_EDITOR_FIND_DIALOG_CLASS
+E_EDITOR_FIND_DIALOG_GET_CLASS
+EEditorFindDialogClass
+e_editor_find_dialog_get_type
+<SUBSECTION Private>
+EEditorFindDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-hrule-dialog</FILE>
+<TITLE>EEditorHRuleDialog</TITLE>
+EEditorHRuleDialog
+e_editor_hrule_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_HRULE_DIALOG
+E_IS_EDITOR_HRULE_DIALOG
+E_TYPE_EDITOR_HRULE_DIALOG
+E_EDITOR_HRULE_DIALOG_CLASS
+E_IS_EDITOR_HRULE_DIALOG_CLASS
+E_EDITOR_HRULE_DIALOG_GET_CLASS
+EEditorHRuleDialogClass
+e_editor_hrule_dialog_get_type
+<SUBSECTION Private>
+EEditorHRuleDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-image-dialog</FILE>
+<TITLE>EEditorImageDialog</TITLE>
+EEditorImageDialog
+e_editor_image_dialog_new
+e_editor_image_dialog_show
+<SUBSECTION Standard>
+E_EDITOR_IMAGE_DIALOG
+E_IS_EDITOR_IMAGE_DIALOG
+E_TYPE_EDITOR_IMAGE_DIALOG
+E_EDITOR_IMAGE_DIALOG_CLASS
+E_IS_EDITOR_IMAGE_DIALOG_CLASS
+E_EDITOR_IMAGE_DIALOG_GET_CLASS
+EEditorImageDialogClass
+e_editor_image_dialog_get_type
+<SUBSECTION Private>
+EEditorImageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-link-dialog</FILE>
+<TITLE>EEditorLinkDialog</TITLE>
+EEditorLinkDialog
+e_editor_link_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_LINK_DIALOG
+E_IS_EDITOR_LINK_DIALOG
+E_TYPE_EDITOR_LINK_DIALOG
+E_EDITOR_LINK_DIALOG_CLASS
+E_IS_EDITOR_LINK_DIALOG_CLASS
+E_EDITOR_LINK_DIALOG_GET_CLASS
+EEditorLinkDialogClass
+e_editor_link_dialog_get_type
+<SUBSECTION Private>
+EEditorLinkDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-page-dialog</FILE>
+<TITLE>EEditorPageDialog</TITLE>
+EEditorPageDialog
+e_editor_page_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_PAGE_DIALOG
+E_IS_EDITOR_PAGE_DIALOG
+E_TYPE_EDITOR_PAGE_DIALOG
+E_EDITOR_PAGE_DIALOG_CLASS
+E_IS_EDITOR_PAGE_DIALOG_CLASS
+E_EDITOR_PAGE_DIALOG_GET_CLASS
+EEditorPageDialogClass
+e_editor_page_dialog_get_type
+<SUBSECTION Private>
+EEditorPageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-paragraph-dialog</FILE>
+<TITLE>EEditorParagraphDialog</TITLE>
+EEditorParagraphDialog
+e_editor_paragraph_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_PARAGRAPH_DIALOG
+E_IS_EDITOR_PARAGRAPH_DIALOG
+E_TYPE_EDITOR_PARAGRAPH_DIALOG
+E_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_IS_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_EDITOR_PARAGRAPH_DIALOG_GET_CLASS
+EEditorParagraphClass
+e_editor_paragraph_dialog_get_type
+<SUBSECTION Private>
+EEditorParagraphDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-replace-dialog</FILE>
+<TITLE>EEditorReplaceDialog</TITLE>
+EEditorReplaceDialog
+e_editor_replace_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_REPLACE_DIALOG
+E_IS_EDITOR_REPLACE_DIALOG
+E_TYPE_EDITOR_REPLACE_DIALOG
+E_EDITOR_REPLACE_DIALOG_CLASS
+E_IS_EDITOR_REPLACE_DIALOG_CLASS
+E_EDITOR_REPLACE_DIALOG_GET_CLASS
+EEditorReplaceDialogClass
+e_editor_replace_dialog_get_type
+<SUBSECTION Private>
+EEditorReplaceDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-selection</FILE>
+<TITLE>EHTMLEditorSelection</TITLE>
+e_html_editor_selection_ref_html_html_editor_view
+e_html_editor_selection_has_text
+e_html_editor_selection_get_caret_word
+e_html_editor_selection_replace_caret_word
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_alignment
+e_html_editor_selection_set_alignment
+e_html_editor_selection_get_background_color
+e_html_editor_selection_set_background_color
+e_html_editor_selection_get_font_color
+e_html_editor_selection_set_font_color
+e_html_editor_selection_get_font_name
+e_html_editor_selection_set_font_name
+EHTMLEditorSelectionFontSize
+e_html_editor_selection_get_font_size
+e_html_editor_selection_set_font_size
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_block_format
+e_html_editor_selection_set_block_format
+e_html_editor_selection_is_citation
+e_html_editor_selection_is_indented
+e_html_editor_selection_indent
+e_html_editor_selection_unindent
+e_html_editor_selection_is_bold
+e_html_editor_selection_set_bold
+e_html_editor_selection_is_italic
+e_html_editor_selection_set_italic
+e_html_editor_selection_is_monospaced
+e_html_editor_selection_set_monospaced
+e_html_editor_selection_is_strike_through
+e_html_editor_selection_set_strike_through
+e_html_editor_selection_is_superscript
+e_html_editor_selection_set_superscript
+e_html_editor_selection_is_subscript
+e_html_editor_selection_set_subscript
+e_html_editor_selection_is_underline
+e_html_editor_selection_set_underline
+e_html_editor_selection_unlink
+e_html_editor_selection_create_link
+e_html_editor_selection_get_string
+e_html_editor_selection_replace
+e_html_editor_selection_insert_html
+e_html_editor_selection_insert_image
+e_html_editor_selection_insert_text
+e_html_editor_selection_wrap_lines
+e_html_editor_selection_save
+e_html_editor_selection_restore
+EHTMLEditorSelectionGranularity
+e_html_editor_selection_move
+e_html_editor_selection_extend
+<SUBSECTION Standard>
+E_HTML_EDITOR_SELECTION
+E_IS_HTML_EDITOR_SELECTION
+E_TYPE_HTML_EDITOR_SELECTION
+E_HTML_EDITOR_SELECTION_CLASS
+E_IS_HTML_EDITOR_SELECTION_CLASS
+E_HTML_EDITOR_SELECTION_GET_CLASS
+EHTMLEditorSelectionClass
+e_html_editor_selection_get_type
+<SUBSECTION Private>
+EHTMLEditorSelectionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-spell-check-dialog</FILE>
+<TITLE>EEditorSpellCheckDialog</TITLE>
+EEditorSpellCheckDialog
+e_editor_spell_check_dialog_new
+e_editor_spell_check_dialog_update_dictionaries
+<SUBSECTION Standard>
+E_EDITOR_SPELL_CHECK_DIALOG
+E_IS_EDITOR_SPELL_CHECK_DIALOG
+E_TYPE_EDITOR_SPELL_CHECK_DIALOG
+E_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_IS_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS
+EEditorSpellCheckDialogClass
+e_editor_spell_check_dialog_get_type
+<SUBSECTION Private>
+EEditorSpellCheckDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-table-dialog</FILE>
+<TITLE>EEditorTableDialog</TITLE>
+EEditorTableDialog
+e_editor_table_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_TABLE_DIALOG
+E_IS_EDITOR_TABLE_DIALOG
+E_TYPE_EDITOR_TABLE_DIALOG
+E_EDITOR_TABLE_DIALOG_CLASS
+E_IS_EDITOR_TABLE_DIALOG_CLASS
+E_EDITOR_TABLE_DIALOG_GET_CLASS
+EEditorTableDialogClass
+e_editor_table_dialog_get_type
+<SUBSECTION Private>
+EEditorTableDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-text-dialog</FILE>
+<TITLE>EEditorTextDialog</TITLE>
+EEditorTextDialog
+e_editor_text_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_TEXT_DIALOG
+E_IS_EDITOR_TEXT_DIALOG
+E_TYPE_EDITOR_TEXT_DIALOG
+E_EDITOR_TEXT_DIALOG_CLASS
+E_IS_EDITOR_TEXT_DIALOG_CLASS
+E_EDITOR_TEXT_DIALOG_GET_CLASS
+EEditorTextDialogClass
+e_editor_text_dialog_get_type
+<SUBSECTION Private>
+EEditorTextDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-widget</FILE>
+<TITLE>EHTMLEditorView</TITLE>
+EHTMLEditorView
+e_html_editor_view_new
+e_html_editor_view_get_selection
+EHTMLEditorViewCommand
+e_html_editor_view_exec_command
+e_html_editor_view_get_changed
+e_html_editor_view_set_changed
+e_html_editor_view_get_html_mode
+e_html_editor_view_set_html_mode
+e_html_editor_view_get_inline_spelling
+e_html_editor_view_set_inline_spelling
+e_html_editor_view_get_magic_links
+e_html_editor_view_set_magic_links
+e_html_editor_view_get_magic_smileys
+e_html_editor_view_set_magic_smileys
+e_html_editor_view_get_spell_checker
+e_html_editor_view_get_text_html
+e_html_editor_view_get_text_plain
+e_html_editor_view_set_text_html
+e_html_editor_view_set_text_plain
+e_html_editor_view_paste_clipboard_quoted
+e_html_editor_view_update_fonts
+<SUBSECTION Standard>
+E_HTML_EDITOR_VIEW
+E_IS_HTML_EDITOR_VIEW
+E_TYPE_HTML_EDITOR_VIEW
+E_HTML_EDITOR_VIEW_CLASS
+E_IS_HTML_EDITOR_VIEW_CLASS
+E_HTML_EDITOR_VIEW_GET_CLASS
+EHTMLEditorViewClass
+e_html_editor_view_get_type
+<SUBSECTION Private>
+EHTMLEditorViewPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon</FILE>
+<TITLE>EEmoticon</TITLE>
+EEmoticon
+e_emoticon_equal
+e_emoticon_copy
+e_emoticon_free
+e_emoticon_get_uri
+<SUBSECTION Standard>
+E_TYPE_EMOTICON
+e_emoticon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-action</FILE>
+<TITLE>EEmoticonAction</TITLE>
+EEmoticonAction
+e_emoticon_action_new
+<SUBSECTION Standard>
+E_EMOTICON_ACTION
+E_IS_EMOTICON_ACTION
+E_TYPE_EMOTICON_ACTION
+E_EMOTICON_ACTION_CLASS
+E_IS_EMOTICON_ACTION_CLASS
+E_EMOTICON_ACTION_GET_CLASS
+EEmoticonActionClass
+e_emoticon_action_get_type
+<SUBSECTION Private>
+EEmoticonActionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser</FILE>
+<TITLE>EEmoticonChooser</TITLE>
+EEmoticonChooser
+EEmoticonChooserInterface
+e_emoticon_chooser_get_current_emoticon
+e_emoticon_chooser_set_current_emoticon
+e_emoticon_chooser_item_activated
+e_emoticon_chooser_get_items
+e_emoticon_chooser_lookup_emoticon
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER
+E_IS_EMOTICON_CHOOSER
+E_TYPE_EMOTICON_CHOOSER
+E_EMOTICON_CHOOSER_GET_INTERFACE
+e_emoticon_chooser_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser-menu</FILE>
+<TITLE>EEmoticonChooserMenu</TITLE>
+EEmoticonChooserMenu
+e_emoticon_chooser_menu_new
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER_MENU
+E_IS_EMOTICON_CHOOSER_MENU
+E_TYPE_EMOTICON_CHOOSER_MENU
+E_EMOTICON_CHOOSER_MENU_CLASS
+E_IS_EMOTICON_CHOOSER_MENU_CLASS
+E_EMOTICON_CHOOSER_MENU_GET_CLASS
+EEmoticonChooserMenuClass
+e_emoticon_chooser_menu_get_type
+<SUBSECTION Private>
+EEmoticonChooserMenuPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-tool-button</FILE>
+<TITLE>EEmoticonToolButton</TITLE>
+EEmoticonToolButton
+e_emoticon_tool_button_new
+e_emoticon_tool_button_popup
+e_emoticon_tool_button_popdown
+<SUBSECTION Standard>
+E_EMOTICON_TOOL_BUTTON
+E_IS_EMOTICON_TOOL_BUTTON
+E_TYPE_EMOTICON_TOOL_BUTTON
+E_EMOTICON_TOOL_BUTTON_CLASS
+E_IS_EMOTICON_TOOL_BUTTON_CLASS
+E_EMOTICON_TOOL_BUTTON_GET_CLASS
+EEmoticonToolButtonClass
+e_emoticon_tool_button_get_type
+<SUBSECTION Private>
+EEmoticonToolButtonPrivate
+</SECTION>
+
+<SECTION>
<FILE>e-event</FILE>
<TITLE>EEvent</TITLE>
EEvent
@@ -1908,6 +2841,25 @@ EImageChooserPrivate
</SECTION>
<SECTION>
+<FILE>e-image-chooser-dialog</FILE>
+<TITLE>EImageChooserDialog</TITLE>
+EImageChooserDialog
+e_image_chooser_dialog_new
+e_image_chooser_dialog_run
+<SUBSECTION Standard>
+E_IMAGE_CHOOSER_DIALOG
+E_IS_IMAGE_CHOOSER_DIALOG
+E_TYPE_IMAGE_CHOOSER_DIALOG
+E_IMAGE_CHOOSER_DIALOG_CLASS
+E_IS_IMAGE_CHOOSER_DIALOG_CLASS
+E_IMAGE_CHOOSER_DIALOG_GET_CLASS
+EImageChooserDialogClass
+e_image_chooser_dialog_get_type
+<SUBSECTION Private>
+EImageChooserDialogPrivate
+</SECTION>
+
+<SECTION>
<FILE>e-import</FILE>
<TITLE>EImport</TITLE>
EImport
@@ -2055,6 +3007,7 @@ EMailSignatureComboBoxPrivate
<TITLE>EMailSignatureEditor</TITLE>
EMailSignatureEditor
e_mail_signature_editor_new
+e_mail_signature_editor_get_editor
e_mail_signature_editor_get_focus_tracker
e_mail_signature_editor_get_registry
e_mail_signature_editor_get_source
@@ -3392,13 +4345,71 @@ e_source_util_remote_delete
</SECTION>
<SECTION>
+<FILE>e-spell-checker</FILE>
+<TITLE>ESpellChecker</TITLE>
+ESpellChecker
+e_spell_checker_new
+e_spell_checker_list_available_dicts
+e_spell_checker_ref_dictionary
+e_spell_checker_get_enchant_dict
+e_spell_checker_get_language_active
+e_spell_checker_set_language_active
+e_spell_checker_list_active_languages
+e_spell_checker_count_active_languages
+e_spell_checker_check_word
+e_spell_checker_learn_word
+e_spell_checker_ignore_word
+<SUBSECTION Standard>
+E_SPELL_CHECKER
+E_IS_SPELL_CHECKER
+E_TYPE_SPELL_CHECKER
+E_SPELL_CHECKER_CLASS
+E_IS_SPELL_CHECKER_CLASS
+E_SPELL_CHECKER_GET_CLASS
+ESpellCheckerClass
+e_spell_checker_get_type
+<SUBSECTION Private>
+ESpellCheckerPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-spell-dictionary</FILE>
+<TITLE>ESpellDictionary</TITLE>
+ESpellDictionary
+e_spell_dictionary_new
+e_spell_dictionary_hash
+e_spell_dictionary_equal
+e_spell_dictionary_compare
+e_spell_dictionary_get_name
+e_spell_dictionary_get_code
+e_spell_dictionary_ref_spell_checker
+e_spell_dictionary_check_word
+e_spell_dictionary_learn_word
+e_spell_dictionary_ignore_word
+e_spell_dictionary_get_suggestions
+e_spell_dictionary_store_correction
+<SUBSECTION Standard>
+E_SPELL_DICTIONARY
+E_IS_SPELL_DICTIONARY
+E_TYPE_SPELL_DICTIONARY
+E_SPELL_DICTIONARY_CLASS
+E_IS_SPELL_DICTIONARY_CLASS
+E_SPELL_DICTIONARY_GET_CLASS
+ESpellDictionaryClass
+e_spell_dictionary_get_type
+<SUBSECTION Private>
+ESpellDictionaryPrivate
+</SECTION>
+
+<SECTION>
<FILE>e-spell-entry</FILE>
<TITLE>ESpellEntry</TITLE>
ESpellEntry
e_spell_entry_new
-e_spell_entry_set_languages
e_spell_entry_get_checking_enabled
e_spell_entry_set_checking_enabled
+e_spell_entry_get_spell_checker
+e_spell_entry_set_spell_checker
<SUBSECTION Standard>
E_SPELL_ENTRY
E_IS_SPELL_ENTRY
@@ -4568,74 +5579,6 @@ EWebViewPrivate
</SECTION>
<SECTION>
-<FILE>e-web-view-gtkhtml</FILE>
-<TITLE>EWebViewGtkHTML</TITLE>
-EWebViewGtkHTML
-e_web_view_gtkhtml_new
-e_web_view_gtkhtml_clear
-e_web_view_gtkhtml_load_string
-e_web_view_gtkhtml_get_animate
-e_web_view_gtkhtml_set_animate
-e_web_view_gtkhtml_get_caret_mode
-e_web_view_gtkhtml_set_caret_mode
-e_web_view_gtkhtml_get_copy_target_list
-e_web_view_gtkhtml_get_disable_printing
-e_web_view_gtkhtml_set_disable_printing
-e_web_view_gtkhtml_get_disable_save_to_disk
-e_web_view_gtkhtml_set_disable_save_to_disk
-e_web_view_gtkhtml_get_editable
-e_web_view_gtkhtml_set_editable
-e_web_view_gtkhtml_get_inline_spelling
-e_web_view_gtkhtml_set_inline_spelling
-e_web_view_gtkhtml_get_magic_links
-e_web_view_gtkhtml_set_magic_links
-e_web_view_gtkhtml_get_magic_smileys
-e_web_view_gtkhtml_set_magic_smileys
-e_web_view_gtkhtml_get_selected_uri
-e_web_view_gtkhtml_set_selected_uri
-e_web_view_gtkhtml_get_cursor_image
-e_web_view_gtkhtml_set_cursor_image
-e_web_view_gtkhtml_get_open_proxy
-e_web_view_gtkhtml_set_open_proxy
-e_web_view_gtkhtml_get_paste_target_list
-e_web_view_gtkhtml_get_print_proxy
-e_web_view_gtkhtml_set_print_proxy
-e_web_view_gtkhtml_get_save_as_proxy
-e_web_view_gtkhtml_set_save_as_proxy
-e_web_view_gtkhtml_get_action
-e_web_view_gtkhtml_get_action_group
-e_web_view_gtkhtml_extract_uri
-e_web_view_gtkhtml_copy_clipboard
-e_web_view_gtkhtml_cut_clipboard
-e_web_view_gtkhtml_is_selection_active
-e_web_view_gtkhtml_paste_clipboard
-e_web_view_gtkhtml_scroll_forward
-e_web_view_gtkhtml_scroll_backward
-e_web_view_gtkhtml_select_all
-e_web_view_gtkhtml_unselect_all
-e_web_view_gtkhtml_zoom_100
-e_web_view_gtkhtml_zoom_in
-e_web_view_gtkhtml_zoom_out
-e_web_view_gtkhtml_get_ui_manager
-e_web_view_gtkhtml_get_popup_menu
-e_web_view_gtkhtml_show_popup_menu
-e_web_view_gtkhtml_status_message
-e_web_view_gtkhtml_stop_loading
-e_web_view_gtkhtml_update_actions
-<SUBSECTION Standard>
-E_WEB_VIEW_GTKHTML
-E_IS_WEB_VIEW_GTKHTML
-E_TYPE_WEB_VIEW_GTKHTML
-E_WEB_VIEW_GTKHTML_CLASS
-E_IS_WEB_VIEW_GTKHTML_CLASS
-E_WEB_VIEW_GTKHTML_GET_CLASS
-EWebViewGtkHTMLClass
-e_web_view_gtkhtml_get_type
-<SUBSECTION Private>
-EWebViewGtkHTMLPrivate
-</SECTION>
-
-<SECTION>
<FILE>e-web-view-preview</FILE>
<TITLE>EWebViewPreview</TITLE>
EWebViewPreview
diff --git a/doc/reference/evolution-util/evolution-util.types b/doc/reference/evolution-util/evolution-util.types
index c47d820386..812856735e 100644
--- a/doc/reference/evolution-util/evolution-util.types
+++ b/doc/reference/evolution-util/evolution-util.types
@@ -55,12 +55,34 @@ e_charset_combo_box_get_type
e_client_cache_get_type
e_client_combo_box_get_type
e_client_selector_get_type
+e_color_chooser_widget_get_type
+e_color_combo_get_type
e_config_get_type
e_config_hook_get_type
e_contact_store_get_type
e_data_capture_get_type
e_date_edit_get_type
e_destination_store_get_type
+e_html_editor_get_type
+e_html_editor_cell_dialog_get_type
+e_html_editor_dialog_get_type
+e_html_editor_find_dialog_get_type
+e_html_editor_hrule_dialog_get_type
+e_html_editor_image_dialog_get_type
+e_html_editor_link_dialog_get_type
+e_html_editor_page_dialog_get_type
+e_html_editor_paragraph_dialog_get_type
+e_html_editor_replace_dialog_get_type
+e_html_editor_selection_get_type
+e_html_editor_spell_check_dialog_get_type
+e_html_editor_table_dialog_get_type
+e_html_editor_text_dialog_get_type
+e_html_editor_view_get_type
+e_emoticon_action_get_type
+e_emoticon_chooser_get_type
+e_emoticon_chooser_menu_get_type
+e_emoticon_get_type
+e_emoticon_tool_button_get_type
e_event_get_type
e_event_hook_get_type
e_file_request_get_type
@@ -75,6 +97,7 @@ e_filter_option_get_type
e_filter_part_get_type
e_filter_rule_get_type
e_focus_tracker_get_type
+e_image_chooser_dialog_get_type
e_image_chooser_get_type
e_import_assistant_get_type
e_import_get_type
@@ -123,6 +146,8 @@ e_source_config_dialog_get_type
e_source_config_get_type
e_source_selector_dialog_get_type
e_source_selector_get_type
+e_spell_checker_get_type
+e_spell_dictionary_get_type
e_spell_entry_get_type
e_stock_request_get_type
e_table_click_to_add_get_type
@@ -163,7 +188,6 @@ e_tree_table_adapter_get_type
e_tree_view_frame_get_type
e_url_entry_get_type
e_web_view_get_type
-e_web_view_gtkhtml_get_type
e_web_view_preview_get_type
gal_view_collection_get_type
gal_view_etable_get_type
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 66cdc220e4..6d9499db27 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -40,6 +40,7 @@ errordir = $(privdatadir)/errors
@EVO_PLUGIN_RULE@
ui_DATA = \
+ e-html-editor-manager.ui \
e-send-options.ui \
e-table-config.ui \
e-timezone-dialog.ui \
@@ -60,6 +61,7 @@ noinst_PROGRAMS = \
test-category-completion \
test-contact-store \
test-dateedit \
+ test-html-editor \
test-mail-signatures \
test-name-selector \
test-preferences-window \
@@ -99,7 +101,7 @@ libevolution_util_la_CPPFLAGS = \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
$(GEO_CFLAGS) \
- $(GTKHTML_CFLAGS) \
+ $(ENCHANT_CFLAGS) \
$(GTKSPELL_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -165,6 +167,8 @@ evolution_util_include_HEADERS = \
e-client-cache.h \
e-client-combo-box.h \
e-client-selector.h \
+ e-color-chooser-widget.h \
+ e-color-combo.h \
e-config.h \
e-contact-store.h \
e-data-capture.h \
@@ -173,6 +177,11 @@ evolution_util_include_HEADERS = \
e-destination-store.h \
e-dialog-utils.h \
e-dialog-widgets.h \
+ e-emoticon-action.h \
+ e-emoticon-chooser-menu.h \
+ e-emoticon-chooser.h \
+ e-emoticon-tool-button.h \
+ e-emoticon.h \
e-event.h \
e-file-request.h \
e-file-utils.h \
@@ -187,9 +196,27 @@ evolution_util_include_HEADERS = \
e-filter-part.h \
e-filter-rule.h \
e-focus-tracker.h \
+ e-html-editor-actions.h \
+ e-html-editor-cell-dialog.h \
+ e-html-editor-dialog.h \
+ e-html-editor-find-dialog.h \
+ e-html-editor-hrule-dialog.h \
+ e-html-editor-image-dialog.h \
+ e-html-editor-link-dialog.h \
+ e-html-editor-page-dialog.h \
+ e-html-editor-paragraph-dialog.h \
+ e-html-editor-replace-dialog.h \
+ e-html-editor-selection.h \
+ e-html-editor-spell-check-dialog.h \
+ e-html-editor-table-dialog.h \
+ e-html-editor-text-dialog.h \
+ e-html-editor-utils.h \
+ e-html-editor-view.h \
+ e-html-editor.h \
e-html-utils.h \
e-icon-factory.h \
e-image-chooser.h \
+ e-image-chooser-dialog.h \
e-import-assistant.h \
e-import.h \
e-interval-chooser.h \
@@ -252,6 +279,8 @@ evolution_util_include_HEADERS = \
e-source-selector-dialog.h \
e-source-selector.h \
e-source-util.h \
+ e-spell-checker.h \
+ e-spell-dictionary.h \
e-spell-entry.h \
e-spell-text-view.h \
e-stock-request.h \
@@ -305,7 +334,6 @@ evolution_util_include_HEADERS = \
e-url-entry.h \
e-util-enums.h \
e-util-enumtypes.h \
- e-web-view-gtkhtml.h \
e-web-view-preview.h \
e-web-view.h \
e-widget-undo.h \
@@ -410,6 +438,8 @@ libevolution_util_la_SOURCES = \
e-client-cache.c \
e-client-combo-box.c \
e-client-selector.c \
+ e-color-chooser-widget.c \
+ e-color-combo.c \
e-config.c \
e-contact-store.c \
e-data-capture.c \
@@ -418,6 +448,11 @@ libevolution_util_la_SOURCES = \
e-destination-store.c \
e-dialog-utils.c \
e-dialog-widgets.c \
+ e-emoticon-action.c \
+ e-emoticon-chooser-menu.c \
+ e-emoticon-chooser.c \
+ e-emoticon-tool-button.c \
+ e-emoticon.c \
e-event.c \
e-file-request.c \
e-file-utils.c \
@@ -432,9 +467,28 @@ libevolution_util_la_SOURCES = \
e-filter-part.c \
e-filter-rule.c \
e-focus-tracker.c \
+ e-html-editor-actions.c \
+ e-html-editor-cell-dialog.c \
+ e-html-editor-dialog.c \
+ e-html-editor-find-dialog.c \
+ e-html-editor-hrule-dialog.c \
+ e-html-editor-image-dialog.c \
+ e-html-editor-link-dialog.c \
+ e-html-editor-page-dialog.c \
+ e-html-editor-paragraph-dialog.c \
+ e-html-editor-private.h \
+ e-html-editor-replace-dialog.c \
+ e-html-editor-selection.c \
+ e-html-editor-spell-check-dialog.c \
+ e-html-editor-table-dialog.c \
+ e-html-editor-text-dialog.c \
+ e-html-editor-utils.c \
+ e-html-editor-view.c \
+ e-html-editor.c \
e-html-utils.c \
e-icon-factory.c \
e-image-chooser.c \
+ e-image-chooser-dialog.c \
e-import-assistant.c \
e-import.c \
e-interval-chooser.c \
@@ -497,6 +551,8 @@ libevolution_util_la_SOURCES = \
e-source-selector-dialog.c \
e-source-selector.c \
e-source-util.c \
+ e-spell-checker.c \
+ e-spell-dictionary.c \
e-spell-entry.c \
e-spell-text-view.c \
e-stock-request.c \
@@ -547,7 +603,6 @@ libevolution_util_la_SOURCES = \
e-url-entry.c \
e-util-enumtypes.c \
e-util-private.h \
- e-web-view-gtkhtml.c \
e-web-view-preview.c \
e-web-view.c \
e-widget-undo.c \
@@ -590,7 +645,7 @@ libevolution_util_la_LIBADD = \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
$(GEO_LIBS) \
- $(GTKHTML_LIBS) \
+ $(ENCHANT_LIBS) \
$(GTKSPELL_LIBS) \
$(INTLLIBS) \
$(MATH_LIB) \
@@ -625,6 +680,10 @@ test_dateedit_CPPFLAGS = $(TEST_CPPFLAGS)
test_dateedit_SOURCES = test-dateedit.c
test_dateedit_LDADD = $(TEST_LDADD)
+test_html_editor_CPPFLAGS = $(TEST_CPPFLAGS)
+test_html_editor_SOURCES = test-html-editor.c
+test_html_editor_LDADD = $(TEST_LDADD)
+
test_mail_signatures_CPPFLAGS = $(TEST_CPPFLAGS)
test_mail_signatures_SOURCES = test-mail-signatures.c
test_mail_signatures_LDADD = $(TEST_LDADD)
diff --git a/e-util/e-action-combo-box.c b/e-util/e-action-combo-box.c
index 1b784b8ee8..33d678ab1e 100644
--- a/e-util/e-action-combo-box.c
+++ b/e-util/e-action-combo-box.c
@@ -104,10 +104,6 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
gboolean visible;
gint width;
- /* Do any of the actions have an icon? */
- if (!combo_box->priv->group_has_icons)
- return;
-
gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
/* A NULL action means the row is a separator. */
@@ -122,8 +118,12 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
"visible", &visible,
NULL);
- /* Keep the pixbuf renderer a fixed size for proper alignment. */
- gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+ /* If some action has an icon */
+ if (combo_box->priv->group_has_icons)
+ /* Keep the pixbuf renderer a fixed size for proper alignment. */
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+ else
+ width = 0;
/* We can't set both "icon-name" and "stock-id" because setting
* one unsets the other. So pick the one that has a non-NULL
@@ -245,13 +245,25 @@ action_combo_box_update_model (EActionComboBox *combo_box)
GtkRadioAction *action = list->data;
GtkTreePath *path;
GtkTreeIter iter;
- gchar *icon_name;
- gchar *stock_id;
+ gchar *icon_name = NULL;
+ gchar *stock_id = NULL;
+ gboolean visible = FALSE;
gint value;
- g_object_get (
- action, "icon-name", &icon_name,
- "stock-id", &stock_id, NULL);
+ g_object_get (action,
+ "icon-name", &icon_name,
+ "stock-id", &stock_id,
+ "visible", &visible,
+ NULL);
+
+ if (!visible) {
+ g_free (icon_name);
+ g_free (stock_id);
+
+ list = g_slist_next (list);
+ continue;
+ }
+
combo_box->priv->group_has_icons |=
(icon_name != NULL || stock_id != NULL);
g_free (icon_name);
@@ -583,3 +595,11 @@ e_action_combo_box_add_separator_after (EActionComboBox *combo_box,
GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
NULL, COLUMN_SORT, (gfloat) action_value + 0.5, -1);
}
+
+void
+e_action_combo_box_update_model (EActionComboBox *combo_box)
+{
+ g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
+
+ action_combo_box_update_model (combo_box);
+}
diff --git a/e-util/e-action-combo-box.h b/e-util/e-action-combo-box.h
index f3a5a0b658..3a44ed730a 100644
--- a/e-util/e-action-combo-box.h
+++ b/e-util/e-action-combo-box.h
@@ -81,6 +81,7 @@ void e_action_combo_box_add_separator_before
void e_action_combo_box_add_separator_after
(EActionComboBox *combo_box,
gint action_value);
+void e_action_combo_box_update_model (EActionComboBox *combo_box);
G_END_DECLS
diff --git a/e-util/e-color-chooser-widget.c b/e-util/e-color-chooser-widget.c
new file mode 100644
index 0000000000..5761ebf2ff
--- /dev/null
+++ b/e-util/e-color-chooser-widget.c
@@ -0,0 +1,253 @@
+
+/* e-color-chooser-widget.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-color-chooser-widget.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_COLOR_CHOOSER_WIDGET_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetPrivate))
+
+/**
+ * EColorChooserWidget:
+ *
+ * This widget wrapps around #GtkColorChooserWidget and allows the widget to be
+ * used as a delegate for #GtkComboBox for instance.
+ */
+
+struct _EColorChooserWidgetPrivate {
+ gboolean showing_editor;
+};
+
+enum {
+ SIGNAL_EDITOR_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+ EColorChooserWidget,
+ e_color_chooser_widget,
+ GTK_TYPE_COLOR_CHOOSER_WIDGET);
+
+/* UGLY UGLY UGLY!
+ * GtkColorChooserWidget sends "color-activated" signal
+ * only when user double-clicks the color. This is totally stupid
+ * and since we want to use it in a combobox-like widget, we need
+ * to be notified upon single click (which by default only selects the color).
+ *
+ * Unfortunatelly the GtkColorSwatch widget, which handles the button-press
+ * event is a non-public widget embedded within the GtkColorChooserWidget,
+ * so we can't just subclass it and fix the behavior.
+ *
+ * Here we override button_press_event of the GtkColorSwatch and manually
+ * emit the 'activate' signal on single click. This is stupid, ugly and I
+ * want to punch someone for such a stupid design...
+ */
+static gboolean
+color_chooser_widget_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if ((event->type == GDK_BUTTON_PRESS) &&
+ (event->button == GDK_BUTTON_PRIMARY)) {
+
+ g_signal_emit_by_name (widget, "activate");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+color_chooser_widget_color_activated (GtkColorChooser *chooser,
+ GdkRGBA *color,
+ gpointer user_data)
+{
+ /* Because we are simulating the double-click by accepting only
+ * single click, the color in the swatch is actually not selected,
+ * so we must do it manually */
+ gtk_color_chooser_set_rgba (chooser, color);
+}
+
+static gboolean
+run_color_chooser_dialog (gpointer user_data)
+{
+ EColorChooserWidgetPrivate *priv;
+ GtkWidget *parent_window;
+ GtkWidget *parent_chooser;
+ GtkWidget *dialog;
+ GtkWidget *chooser;
+
+ parent_chooser = user_data;
+
+ g_object_set (
+ G_OBJECT (parent_chooser), "show-editor", FALSE, NULL);
+
+ parent_window = g_object_get_data (G_OBJECT (parent_chooser), "window");
+ if (!parent_window)
+ parent_window = gtk_widget_get_toplevel (parent_chooser);
+ dialog = gtk_dialog_new_with_buttons (
+ N_("Choose custom color"),
+ GTK_WINDOW (parent_window),
+ GTK_DIALOG_MODAL,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
+
+ chooser = gtk_color_chooser_widget_new ();
+ g_object_set (G_OBJECT (chooser), "show-editor", TRUE, NULL);
+ gtk_box_pack_start (
+ GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ chooser, TRUE, TRUE, 5);
+
+ gtk_widget_show_all (chooser);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ GdkRGBA color;
+
+ gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (chooser), &color);
+ gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (parent_chooser), &color);
+
+ g_signal_emit_by_name (parent_chooser, "color-activated", &color);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ priv = E_COLOR_CHOOSER_WIDGET_GET_PRIVATE (parent_chooser);
+ priv->showing_editor = FALSE;
+
+ return FALSE;
+}
+
+static void
+color_chooser_show_editor_notify_cb (EColorChooserWidget *chooser,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ gboolean show_editor;
+
+ g_object_get (G_OBJECT (chooser), "show-editor", &show_editor, NULL);
+
+ /* Nothing to do here... */
+ if ((show_editor == FALSE) || (chooser->priv->showing_editor == TRUE))
+ return;
+
+ chooser->priv->showing_editor = TRUE;
+
+ /* Hide the editor - we don't want to display the single-color editor
+ * within this widget. We rather create a dialog window with the editor
+ * (we can't do it from this callback as Gtk would stop it in order to
+ * prevent endless recursion probably) */
+ g_idle_add (run_color_chooser_dialog, chooser);
+ g_signal_emit (chooser, signals[SIGNAL_EDITOR_ACTIVATED], 0);
+}
+
+void
+e_color_chooser_widget_class_init (EColorChooserWidgetClass *class)
+{
+ g_type_class_add_private (class, sizeof (EColorChooserWidgetPrivate));
+
+ signals[SIGNAL_EDITOR_ACTIVATED] = g_signal_new (
+ "editor-activated",
+ E_TYPE_COLOR_CHOOSER_WIDGET,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EColorChooserWidgetClass, editor_activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/* Recursively go through GtkContainers within the GtkColorChooserWidget
+ * and try to find GtkColorSwatch widget. */
+static GtkWidget *
+find_swatch (GtkContainer *container)
+{
+ GList *children, *child;
+
+ children = gtk_container_get_children (container);
+ for (child = children; child; child = g_list_next (child)) {
+ GtkWidget *widget = child->data;
+ GtkWidget *swatch;
+
+ if (GTK_IS_CONTAINER (widget)) {
+ swatch = find_swatch (GTK_CONTAINER (widget));
+
+ if (swatch != NULL) {
+ g_list_free (children);
+ return swatch;
+ }
+ }
+
+ if (g_strcmp0 (G_OBJECT_TYPE_NAME (widget), "GtkColorSwatch") == 0) {
+ g_list_free (children);
+ return widget;
+ }
+ }
+
+ g_list_free (children);
+
+ return NULL;
+}
+
+void
+e_color_chooser_widget_init (EColorChooserWidget *widget)
+{
+ GtkWidget *swatch;
+
+ widget->priv = E_COLOR_CHOOSER_WIDGET_GET_PRIVATE (widget);
+ widget->priv->showing_editor = FALSE;
+
+ swatch = find_swatch (GTK_CONTAINER (widget));
+
+ /* If swatch is NULL then GTK changed something and this widget
+ * becomes broken... */
+ g_return_if_fail (swatch != NULL);
+
+ if (swatch) {
+ GtkWidgetClass *swatch_class;
+ swatch_class = GTK_WIDGET_GET_CLASS (swatch);
+ swatch_class->button_press_event = color_chooser_widget_button_press_event;
+ }
+
+ g_signal_connect (
+ widget, "color-activated",
+ G_CALLBACK (color_chooser_widget_color_activated), NULL);
+
+ g_signal_connect (
+ widget, "notify::show-editor",
+ G_CALLBACK (color_chooser_show_editor_notify_cb), NULL);
+}
+
+GtkWidget *
+e_color_chooser_widget_new (void)
+{
+ return g_object_new (
+ E_TYPE_COLOR_CHOOSER_WIDGET,
+ "show-editor", FALSE,
+ "use-alpha", FALSE,
+ NULL);
+}
diff --git a/e-util/e-color-chooser-widget.h b/e-util/e-color-chooser-widget.h
new file mode 100644
index 0000000000..3b7c60de7f
--- /dev/null
+++ b/e-util/e-color-chooser-widget.h
@@ -0,0 +1,71 @@
+/* e-color-chooser-widget.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_COLOR_CHOOSER_WIDGET_H
+#define E_COLOR_CHOOSER_WIDGET_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COLOR_CHOOSER_WIDGET \
+ (e_color_chooser_widget_get_type ())
+#define E_COLOR_CHOOSER_WIDGET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidget))
+#define E_COLOR_CHOOSER_WIDGET_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetClass))
+#define E_IS_COLOR_CHOOSER_WIDGET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_COLOR_CHOOSER_WIDGET))
+#define E_IS_COLOR_CHOOSER_WIDGET_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_COLOR_CHOOSER_WIDGET))
+#define E_COLOR_CHOOSER_WIDGET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EColorChooserWidget EColorChooserWidget;
+typedef struct _EColorChooserWidgetClass EColorChooserWidgetClass;
+typedef struct _EColorChooserWidgetPrivate EColorChooserWidgetPrivate;
+
+struct _EColorChooserWidget {
+ GtkColorChooserWidget parent;
+ EColorChooserWidgetPrivate *priv;
+};
+
+struct _EColorChooserWidgetClass {
+ GtkColorChooserWidgetClass parent_class;
+
+ void (*editor_activated) (GtkColorChooserWidget *chooser);
+};
+
+GType e_color_chooser_widget_get_type (void) G_GNUC_CONST;
+GtkWidget * e_color_chooser_widget_new (void);
+
+G_END_DECLS
+
+#endif /* E_COLOR_CHOOSER_WIDGET_H */
+
diff --git a/e-util/e-color-combo.c b/e-util/e-color-combo.c
new file mode 100644
index 0000000000..f2f46e4bec
--- /dev/null
+++ b/e-util/e-color-combo.c
@@ -0,0 +1,976 @@
+/* e-color-combo.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-color-combo.h"
+#include "e-color-chooser-widget.h"
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <cairo/cairo.h>
+#include <alloca.h>
+
+#define E_COLOR_COMBO_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_COLOR_COMBO, EColorComboPrivate))
+
+struct _EColorComboPrivate {
+ GtkWidget *color_frame; /* not referenced */
+ GtkWidget *arrow; /* not referenced */
+
+ GtkWidget *window;
+ GtkWidget *default_button; /* not referenced */
+ GtkWidget *chooser_widget; /* not referenced */
+
+ guint popup_shown : 1;
+ guint popup_in_progress : 1;
+
+ GdkRGBA *current_color;
+ GdkRGBA *default_color;
+ gint default_transparent: 1;
+
+ GList *palette;
+
+ GdkDevice *grab_keyboard;
+ GdkDevice *grab_mouse;
+};
+
+enum {
+ PROP_0,
+ PROP_CURRENT_COLOR,
+ PROP_DEFAULT_COLOR,
+ PROP_DEFAULT_LABEL,
+ PROP_DEFAULT_TRANSPARENT,
+ PROP_PALETTE,
+ PROP_POPUP_SHOWN
+};
+
+enum {
+ ACTIVATED,
+ POPUP,
+ POPDOWN,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+static GdkRGBA black = { 0, 0, 0, 1 };
+
+static struct {
+ const gchar *color;
+ const gchar *tooltip;
+} default_colors[] = {
+
+ { "#000000", N_("black") },
+ { "#993300", N_("light brown") },
+ { "#333300", N_("brown gold") },
+ { "#003300", N_("dark green #2") },
+ { "#003366", N_("navy") },
+ { "#000080", N_("dark blue") },
+ { "#333399", N_("purple #2") },
+ { "#333333", N_("very dark gray") },
+
+ { "#800000", N_("dark red") },
+ { "#FF6600", N_("red-orange") },
+ { "#808000", N_("gold") },
+ { "#008000", N_("dark green") },
+ { "#008080", N_("dull blue") },
+ { "#0000FF", N_("blue") },
+ { "#666699", N_("dull purple") },
+ { "#808080", N_("dark grey") },
+
+ { "#FF0000", N_("red") },
+ { "#FF9900", N_("orange") },
+ { "#99CC00", N_("lime") },
+ { "#339966", N_("dull green") },
+ { "#33CCCC", N_("dull blue #2") },
+ { "#3366FF", N_("sky blue #2") },
+ { "#800080", N_("purple") },
+ { "#969696", N_("gray") },
+
+ { "#FF00FF", N_("magenta") },
+ { "#FFCC00", N_("bright orange") },
+ { "#FFFF00", N_("yellow") },
+ { "#00FF00", N_("green") },
+ { "#00FFFF", N_("cyan") },
+ { "#00CCFF", N_("bright blue") },
+ { "#993366", N_("red purple") },
+ { "#C0C0C0", N_("light grey") },
+
+ { "#FF99CC", N_("pink") },
+ { "#FFCC99", N_("light orange") },
+ { "#FFFF99", N_("light yellow") },
+ { "#CCFFCC", N_("light green") },
+ { "#CCFFFF", N_("light cyan") },
+ { "#99CCFF", N_("light blue") },
+ { "#CC99FF", N_("light purple") },
+ { "#FFFFFF", N_("white") }
+};
+
+G_DEFINE_TYPE (
+ EColorCombo,
+ e_color_combo,
+ GTK_TYPE_BUTTON);
+
+static void
+color_combo_reposition_window (EColorCombo *combo)
+{
+ GdkScreen *screen;
+ GdkWindow *window;
+ GdkRectangle monitor;
+ GtkAllocation allocation;
+ gint monitor_num;
+ gint x, y, width, height;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (combo));
+ window = gtk_widget_get_window (GTK_WIDGET (combo));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (window, &x, &y);
+
+ if (!gtk_widget_get_has_window (GTK_WIDGET (combo))) {
+ gtk_widget_get_allocation (GTK_WIDGET (combo), &allocation);
+ x += allocation.x;
+ y += allocation.y;
+ }
+
+ gtk_widget_get_allocation (combo->priv->window, &allocation);
+ width = allocation.width;
+ height = allocation.height;
+
+ x = CLAMP (x, monitor.x, monitor.x + monitor.width - width);
+ y = CLAMP (y, monitor.y, monitor.y + monitor.height - height);
+
+ gtk_window_move (GTK_WINDOW (combo->priv->window), x, y);
+}
+
+static void
+color_combo_popup (EColorCombo *combo)
+{
+ GdkWindow *window;
+ gboolean grab_status;
+ GdkDevice *device, *mouse, *keyboard;
+ guint32 activate_time;
+
+ device = gtk_get_current_event_device ();
+ g_return_if_fail (device != NULL);
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
+ return;
+
+ if (combo->priv->popup_shown)
+ return;
+
+ activate_time = gtk_get_current_event_time ();
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) {
+ keyboard = device;
+ mouse = gdk_device_get_associated_device (device);
+ } else {
+ keyboard = gdk_device_get_associated_device (device);
+ mouse = device;
+ }
+
+ /* Position the window over the button. */
+ color_combo_reposition_window (combo);
+
+ /* Show the pop-up. */
+ gtk_widget_show_all (combo->priv->window);
+ gtk_widget_grab_focus (combo->priv->window);
+
+ /* Try to grab the pointer and keyboard. */
+ window = gtk_widget_get_window (combo->priv->window);
+ grab_status =
+ (keyboard == NULL) ||
+ (gdk_device_grab (
+ keyboard, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, activate_time) == GDK_GRAB_SUCCESS);
+ if (grab_status) {
+ grab_status =
+ (mouse == NULL) ||
+ (gdk_device_grab (
+ mouse, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, activate_time) == GDK_GRAB_SUCCESS);
+ if (!grab_status && keyboard)
+ gdk_device_ungrab (keyboard, activate_time);
+ }
+
+ if (grab_status) {
+ gtk_device_grab_add (combo->priv->window, mouse, TRUE);
+ combo->priv->grab_keyboard = keyboard;
+ combo->priv->grab_mouse = mouse;
+ } else {
+ gtk_widget_hide (combo->priv->window);
+ }
+
+ /* Always make sure the editor-mode is OFF */
+ g_object_set (
+ G_OBJECT (combo->priv->chooser_widget),
+ "show-editor", FALSE, NULL);
+}
+
+static void
+color_combo_popdown (EColorCombo *combo)
+{
+ if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
+ return;
+
+ if (!combo->priv->popup_shown)
+ return;
+
+ /* Hide the pop-up. */
+ gtk_device_grab_remove (combo->priv->window, combo->priv->grab_mouse);
+ gtk_widget_hide (combo->priv->window);
+
+ if (combo->priv->grab_keyboard)
+ gdk_device_ungrab (combo->priv->grab_keyboard, GDK_CURRENT_TIME);
+ if (combo->priv->grab_mouse)
+ gdk_device_ungrab (combo->priv->grab_mouse, GDK_CURRENT_TIME);
+
+ combo->priv->grab_keyboard = NULL;
+ combo->priv->grab_mouse = NULL;
+}
+
+static gboolean
+color_combo_window_button_press_event_cb (EColorCombo *combo,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GtkWidget *event_widget;
+
+ event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+ if (event_widget == combo->priv->window)
+ return TRUE;
+
+ if (combo->priv->popup_shown == TRUE)
+ return FALSE;
+
+ color_combo_popup (combo);
+
+ combo->priv->popup_in_progress = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+color_combo_window_button_release_event_cb (EColorCombo *combo,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ gboolean popup_in_progress;
+
+ popup_in_progress = combo->priv->popup_in_progress;
+ combo->priv->popup_in_progress = FALSE;
+
+ if (popup_in_progress)
+ return FALSE;
+
+ if (combo->priv->popup_shown)
+ goto popdown;
+
+ return FALSE;
+
+popdown:
+ color_combo_popdown (combo);
+
+ return TRUE;
+}
+
+static void
+color_combo_child_show_cb (EColorCombo *combo)
+{
+ combo->priv->popup_shown = TRUE;
+ g_object_notify (G_OBJECT (combo), "popup-shown");
+}
+
+static void
+color_combo_child_hide_cb (EColorCombo *combo)
+{
+ combo->priv->popup_shown = FALSE;
+ g_object_notify (G_OBJECT (combo), "popup-shown");
+}
+
+static void
+color_combo_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *natural_width)
+{
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (e_color_combo_parent_class);
+ widget_class->get_preferred_width (widget, min_width, natural_width);
+
+ /* Make sure the box with color sample is always visible */
+ if (min_width)
+ *min_width += 20;
+
+ if (natural_width)
+ *natural_width += 20;
+}
+
+static gboolean
+color_combo_button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EColorCombo *combo = E_COLOR_COMBO (widget);
+ GdkWindow *window;
+ gint x, y, width, height;
+
+ window = gtk_widget_get_window (combo->priv->color_frame);
+ gdk_window_get_position (window, &x, &y);
+ /* Width - only width of the frame with color box */
+ width = gtk_widget_get_allocated_width (combo->priv->color_frame);
+
+ /* Height - height of the entire button (widget) */
+ height = gtk_widget_get_allocated_height (widget);
+
+ /* Check whether user clicked on the color frame - in such case
+ * apply the color immediatelly without displaying the popup widget */
+ if ((event->x_root >= x) && (event->x_root <= x + width) &&
+ (event->y_root >= y) && (event->y_root <= y + height)) {
+ GdkRGBA color;
+
+ e_color_combo_get_current_color (combo, &color);
+ g_signal_emit (combo, signals[ACTIVATED], 0, &color);
+
+ return TRUE;
+ }
+
+ /* Otherwise display the popup widget */
+ if (combo->priv->popup_shown) {
+ color_combo_popdown (combo);
+ } else {
+ color_combo_popup (combo);
+ }
+
+ return FALSE;
+}
+
+static void
+color_combo_swatch_color_changed (EColorCombo *combo,
+ GdkRGBA *color,
+ gpointer user_data)
+{
+ g_signal_emit (combo, signals[ACTIVATED], 0, color);
+
+ e_color_combo_set_current_color (combo, color);
+
+ color_combo_popdown (combo);
+}
+
+static void
+color_combo_draw_frame_cb (GtkWidget *widget,
+ cairo_t *cr,
+ gpointer user_data)
+{
+ EColorCombo *combo = user_data;
+ GdkRGBA rgba;
+ GtkAllocation allocation;
+ gint height, width;
+
+ e_color_combo_get_current_color (combo, &rgba);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ width = allocation.width;
+ height = allocation.height;
+
+ cairo_rectangle (cr, 0, 0, width - 10, height);
+ cairo_set_source_rgb (cr, rgba.red, rgba.green, rgba.blue);
+ cairo_fill (cr);
+}
+
+static void
+color_combo_set_default_color_cb (EColorCombo *combo,
+ gpointer user_data)
+{
+ GdkRGBA color;
+
+ e_color_combo_get_default_color (combo, &color);
+ e_color_combo_set_current_color (combo, &color);
+
+ g_signal_emit (combo, signals[ACTIVATED], 0, &color);
+}
+
+static void
+color_combo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_COLOR:
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (object),
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_DEFAULT_COLOR:
+ e_color_combo_set_default_color (
+ E_COLOR_COMBO (object),
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_DEFAULT_LABEL:
+ e_color_combo_set_default_label (
+ E_COLOR_COMBO (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_DEFAULT_TRANSPARENT:
+ e_color_combo_set_default_transparent (
+ E_COLOR_COMBO (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_PALETTE:
+ e_color_combo_set_palette (
+ E_COLOR_COMBO (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_POPUP_SHOWN:
+ if (g_value_get_boolean (value))
+ e_color_combo_popup (
+ E_COLOR_COMBO (object));
+ else
+ e_color_combo_popdown (
+ E_COLOR_COMBO (object));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+color_combo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EColorComboPrivate *priv;
+ GdkRGBA color;
+
+ priv = E_COLOR_COMBO_GET_PRIVATE (object);
+
+ switch (property_id) {
+ case PROP_CURRENT_COLOR:
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (object), &color);
+ g_value_set_boxed (value, &color);
+ return;
+
+ case PROP_DEFAULT_COLOR:
+ e_color_combo_get_default_color (
+ E_COLOR_COMBO (object), &color);
+ g_value_set_boxed (value, &color);
+ return;
+
+ case PROP_DEFAULT_LABEL:
+ g_value_set_string (
+ value, e_color_combo_get_default_label (
+ E_COLOR_COMBO (object)));
+ return;
+
+ case PROP_DEFAULT_TRANSPARENT:
+ g_value_set_boolean (
+ value,
+ e_color_combo_get_default_transparent (
+ E_COLOR_COMBO (object)));
+ return;
+
+ case PROP_PALETTE:
+ g_value_set_object (
+ value, e_color_combo_get_palette (
+ E_COLOR_COMBO (object)));
+ return;
+
+ case PROP_POPUP_SHOWN:
+ g_value_set_boolean (value, priv->popup_shown);
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+color_combo_dispose (GObject *object)
+{
+ EColorComboPrivate *priv;
+
+ priv = E_COLOR_COMBO_GET_PRIVATE (object);
+
+ if (priv->window != NULL) {
+ gtk_widget_destroy (priv->window);
+ priv->window = NULL;
+ }
+
+ if (priv->current_color != NULL) {
+ gdk_rgba_free (priv->current_color);
+ priv->current_color = NULL;
+ }
+
+ if (priv->default_color != NULL) {
+ gdk_rgba_free (priv->default_color);
+ priv->default_color = NULL;
+ }
+
+ g_list_free_full (priv->palette, (GDestroyNotify) gdk_rgba_free);
+ priv->palette = NULL;
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_color_combo_parent_class)->dispose (object);
+}
+
+static void
+e_color_combo_class_init (EColorComboClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EColorComboPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = color_combo_set_property;
+ object_class->get_property = color_combo_get_property;
+ object_class->dispose = color_combo_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->get_preferred_width = color_combo_get_preferred_width;
+ widget_class->button_press_event = color_combo_button_press_event_cb;
+
+ class->popup = color_combo_popup;
+ class->popdown = color_combo_popdown;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_COLOR,
+ g_param_spec_boxed (
+ "current-color",
+ "Current color",
+ "The currently selected color",
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DEFAULT_COLOR,
+ g_param_spec_boxed (
+ "default-color",
+ "Default color",
+ "The color associated with the default button",
+ GDK_TYPE_RGBA,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DEFAULT_LABEL,
+ g_param_spec_string (
+ "default-label",
+ "Default label",
+ "The label for the default button",
+ _("Default"),
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DEFAULT_TRANSPARENT,
+ g_param_spec_boolean (
+ "default-transparent",
+ "Default is transparent",
+ "Whether the default color is transparent",
+ FALSE,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PALETTE,
+ g_param_spec_pointer (
+ "palette",
+ "Color palette",
+ "Custom color palette",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_POPUP_SHOWN,
+ g_param_spec_boolean (
+ "popup-shown",
+ "Popup shown",
+ "Whether the combo's dropdown is shown",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ signals[ACTIVATED] = g_signal_new (
+ "activated",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EColorComboClass, activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[POPUP] = g_signal_new (
+ "popup",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EColorComboClass, popup),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[POPDOWN] = g_signal_new (
+ "popdown",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EColorComboClass, popdown),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Escape, 0, "popdown", 0);
+}
+
+static void
+e_color_combo_init (EColorCombo *combo)
+{
+ GtkWidget *container;
+ GtkWidget *toplevel;
+ GtkWidget *widget;
+ GList *palette;
+ guint ii;
+
+ combo->priv = E_COLOR_COMBO_GET_PRIVATE (combo);
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (combo), widget);
+
+ container = widget;
+
+ /* Build the combo button. */
+ widget = gtk_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ g_signal_connect (
+ widget, "draw",
+ G_CALLBACK (color_combo_draw_frame_cb), combo);
+ combo->priv->color_frame = widget; /* do not reference */
+
+ widget = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+
+ widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ combo->priv->arrow = widget; /* do not reference */
+
+ /* Build the drop-down menu */
+ widget = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 5);
+ gtk_window_set_resizable (GTK_WINDOW (widget), FALSE);
+ gtk_window_set_type_hint (
+ GTK_WINDOW (widget), GDK_WINDOW_TYPE_HINT_COMBO);
+ combo->priv->window = g_object_ref_sink (widget);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo));
+ if (GTK_IS_WINDOW (toplevel)) {
+ gtk_window_group_add_window (
+ gtk_window_get_group (GTK_WINDOW (toplevel)),
+ GTK_WINDOW (widget));
+ gtk_window_set_transient_for (
+ GTK_WINDOW (widget), GTK_WINDOW (toplevel));
+ }
+
+ g_signal_connect_swapped (
+ widget, "show",
+ G_CALLBACK (color_combo_child_show_cb), combo);
+ g_signal_connect_swapped (
+ widget, "hide",
+ G_CALLBACK (color_combo_child_hide_cb), combo);
+ g_signal_connect_swapped (
+ widget, "button-press-event",
+ G_CALLBACK (color_combo_window_button_press_event_cb), combo);
+ g_signal_connect_swapped (
+ widget, "button-release-event",
+ G_CALLBACK (color_combo_window_button_release_event_cb), combo);
+
+ container = widget;
+
+ widget = gtk_grid_new ();
+ gtk_grid_set_row_spacing (GTK_GRID (widget), 5);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+
+ container = widget;
+
+ widget = gtk_button_new ();
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
+ combo->priv->default_button = widget; /* do not reference */
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (color_combo_set_default_color_cb), combo);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (color_combo_popdown), combo);
+
+ widget = e_color_chooser_widget_new ();
+ g_object_set_data (G_OBJECT (widget), "window", combo->priv->window);
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1);
+ combo->priv->chooser_widget = widget; /* do not reference */
+
+ g_signal_connect_swapped (
+ widget, "color-activated",
+ G_CALLBACK (color_combo_swatch_color_changed), combo);
+ g_signal_connect_swapped (
+ widget, "editor-activated",
+ G_CALLBACK (color_combo_popdown), combo);
+
+ palette = NULL;
+ for (ii = 0; ii < G_N_ELEMENTS (default_colors); ii++) {
+ GdkRGBA *color = g_new0 (GdkRGBA, 1);
+ gdk_rgba_parse (color, default_colors[ii].color);
+
+ palette = g_list_prepend (palette, color);
+ }
+ palette = g_list_reverse (palette);
+ e_color_combo_set_palette (combo, palette);
+ g_list_free_full (palette, (GDestroyNotify) g_free);
+
+ combo->priv->current_color = gdk_rgba_copy (&black);
+ combo->priv->default_color = gdk_rgba_copy (&black);
+}
+
+GtkWidget *
+e_color_combo_new (void)
+{
+ return g_object_new (E_TYPE_COLOR_COMBO, NULL);
+}
+
+GtkWidget *
+e_color_combo_new_defaults (GdkRGBA *default_color,
+ const gchar *default_label)
+{
+ g_return_val_if_fail (default_color != NULL, NULL);
+ g_return_val_if_fail (default_label != NULL, NULL);
+
+ return g_object_new (
+ E_TYPE_COLOR_COMBO,
+ "default-color", default_color,
+ "default-label", default_label,
+ NULL);
+}
+
+void
+e_color_combo_popup (EColorCombo *combo)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ g_signal_emit (combo, signals[POPUP], 0);
+}
+
+void
+e_color_combo_popdown (EColorCombo *combo)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ g_signal_emit (combo, signals[POPDOWN], 0);
+}
+
+void
+e_color_combo_get_current_color (EColorCombo *combo,
+ GdkRGBA *color)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+ g_return_if_fail (color != NULL);
+
+ color->red = combo->priv->current_color->red;
+ color->green = combo->priv->current_color->green;
+ color->blue = combo->priv->current_color->blue;
+ color->alpha = combo->priv->current_color->alpha;
+}
+
+void
+e_color_combo_set_current_color (EColorCombo *combo,
+ const GdkRGBA *color)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ if (color == NULL)
+ color = &black;
+
+ if (combo->priv->current_color) {
+
+ if (gdk_rgba_equal (color, combo->priv->current_color)) {
+ return;
+ }
+
+ gdk_rgba_free (combo->priv->current_color);
+ }
+
+ combo->priv->current_color = gdk_rgba_copy (color);
+
+ gtk_color_chooser_set_rgba (
+ GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
+ gtk_widget_queue_draw (combo->priv->color_frame);
+
+ g_object_notify (G_OBJECT (combo), "current-color");
+}
+
+void
+e_color_combo_get_default_color (EColorCombo *combo,
+ GdkRGBA *color)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+ g_return_if_fail (color != NULL);
+
+ color->red = combo->priv->default_color->red;
+ color->green = combo->priv->default_color->green;
+ color->blue = combo->priv->default_color->blue;
+ color->alpha = combo->priv->default_color->alpha;
+}
+
+void
+e_color_combo_set_default_color (EColorCombo *combo,
+ const GdkRGBA *color)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ if (color == NULL)
+ color = &black;
+
+ if (combo->priv->default_color) {
+
+ if (gdk_rgba_equal (color, combo->priv->default_color)) {
+ return;
+ }
+
+ gdk_rgba_free (combo->priv->default_color);
+ }
+ combo->priv->default_color = gdk_rgba_copy (color);
+
+ gtk_color_chooser_set_rgba (
+ GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
+
+ g_object_notify (G_OBJECT (combo), "default-color");
+}
+
+const gchar *
+e_color_combo_get_default_label (EColorCombo *combo)
+{
+ g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
+
+ return gtk_button_get_label (GTK_BUTTON (combo->priv->default_button));
+}
+
+void
+e_color_combo_set_default_label (EColorCombo *combo,
+ const gchar *text)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ gtk_button_set_label (GTK_BUTTON (combo->priv->default_button), text);
+
+ g_object_notify (G_OBJECT (combo), "default-label");
+}
+
+gboolean
+e_color_combo_get_default_transparent (EColorCombo *combo)
+{
+ g_return_val_if_fail (E_IS_COLOR_COMBO (combo), FALSE);
+
+ return combo->priv->default_transparent;
+}
+
+void
+e_color_combo_set_default_transparent (EColorCombo *combo,
+ gboolean transparent)
+{
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ combo->priv->default_transparent = transparent;
+
+ g_object_notify (G_OBJECT (combo), "default-transparent");
+}
+
+GList *
+e_color_combo_get_palette (EColorCombo *combo)
+{
+ g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
+
+ return g_list_copy (combo->priv->palette);
+}
+
+void
+e_color_combo_set_palette (EColorCombo *combo,
+ GList *palette)
+{
+ gint ii, count, colors_per_line;
+ GList *iter;
+ GdkRGBA *colors;
+
+ g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+ count = g_list_length (palette);
+ colors_per_line = (count % 10 == 0) ? 10 : 9;
+
+ colors = g_malloc_n (count, sizeof (GdkRGBA));
+ g_list_free_full (combo->priv->palette, (GDestroyNotify) gdk_rgba_free);
+ ii = 0;
+ combo->priv->palette = NULL;
+ for (iter = palette; iter; iter = g_list_next (iter)) {
+ combo->priv->palette = g_list_prepend (
+ combo->priv->palette, gdk_rgba_copy (iter->data));
+
+ colors[ii] = *((GdkRGBA *) iter->data);
+ ii++;
+ }
+ combo->priv->palette = g_list_reverse (combo->priv->palette);
+
+ gtk_color_chooser_add_palette (
+ GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
+ GTK_ORIENTATION_HORIZONTAL, 0, 0, NULL);
+ gtk_color_chooser_add_palette (
+ GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
+ GTK_ORIENTATION_HORIZONTAL, colors_per_line, count, colors);
+ g_free (colors);
+}
diff --git a/e-util/e-color-combo.h b/e-util/e-color-combo.h
new file mode 100644
index 0000000000..41f7fd1574
--- /dev/null
+++ b/e-util/e-color-combo.h
@@ -0,0 +1,96 @@
+/* e-color-combo.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_COLOR_COMBO_H
+#define E_COLOR_COMBO_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COLOR_COMBO \
+ (e_color_combo_get_type ())
+#define E_COLOR_COMBO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_COLOR_COMBO, EColorCombo))
+#define E_COLOR_COMBO_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_COLOR_COMBO, EColorComboClass))
+#define E_IS_COLOR_COMBO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_COLOR_COMBO))
+#define E_IS_COLOR_COMBO_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_COLOR_COMBO))
+#define E_COLOR_COMBO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_COLOR_COMBO, EColorComboClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EColorCombo EColorCombo;
+typedef struct _EColorComboClass EColorComboClass;
+typedef struct _EColorComboPrivate EColorComboPrivate;
+
+struct _EColorCombo {
+ GtkButton parent;
+ EColorComboPrivate *priv;
+};
+
+struct _EColorComboClass {
+ GtkButtonClass parent_class;
+
+ void (*popup) (EColorCombo *combo);
+ void (*popdown) (EColorCombo *combo);
+ void (*activated) (EColorCombo *combo,
+ GdkRGBA *color);
+};
+
+GType e_color_combo_get_type (void) G_GNUC_CONST;
+GtkWidget * e_color_combo_new (void);
+GtkWidget * e_color_combo_new_defaults (GdkRGBA *default_color,
+ const gchar *default_label);
+void e_color_combo_popup (EColorCombo *combo);
+void e_color_combo_popdown (EColorCombo *combo);
+void e_color_combo_get_current_color (EColorCombo *combo,
+ GdkRGBA *rgba);
+void e_color_combo_set_current_color (EColorCombo *combo,
+ const GdkRGBA *color);
+void e_color_combo_get_default_color (EColorCombo *combo,
+ GdkRGBA *color);
+void e_color_combo_set_default_color (EColorCombo *combo,
+ const GdkRGBA *default_color);
+const gchar * e_color_combo_get_default_label (EColorCombo *combo);
+void e_color_combo_set_default_label (EColorCombo *combo,
+ const gchar *text);
+gboolean e_color_combo_get_default_transparent
+ (EColorCombo *combo);
+void e_color_combo_set_default_transparent
+ (EColorCombo *combo,
+ gboolean transparent);
+GList * e_color_combo_get_palette (EColorCombo *combo);
+void e_color_combo_set_palette (EColorCombo *combo,
+ GList *palette);
+
+G_END_DECLS
+
+#endif /* E_COLOR_COMBO_H */
diff --git a/e-util/e-emoticon-action.c b/e-util/e-emoticon-action.c
new file mode 100644
index 0000000000..0850d33f49
--- /dev/null
+++ b/e-util/e-emoticon-action.c
@@ -0,0 +1,278 @@
+/*
+ * e-emoticon-action.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon-action.h"
+
+#include "e-emoticon-chooser.h"
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-tool-button.h"
+
+#define E_EMOTICON_ACTION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionPrivate))
+
+struct _EEmoticonActionPrivate {
+ GList *choosers;
+ EEmoticonChooser *current_chooser;
+};
+
+enum {
+ PROP_0,
+ PROP_CURRENT_FACE
+};
+
+/* Forward Declarations */
+static void e_emoticon_action_interface_init
+ (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EEmoticonAction,
+ e_emoticon_action,
+ GTK_TYPE_ACTION,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EMOTICON_CHOOSER,
+ e_emoticon_action_interface_init))
+
+static void
+emoticon_action_proxy_item_activated_cb (EEmoticonAction *action,
+ EEmoticonChooser *chooser)
+{
+ action->priv->current_chooser = chooser;
+
+ g_signal_emit_by_name (action, "item-activated");
+}
+
+static void
+emoticon_action_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FACE:
+ e_emoticon_chooser_set_current_emoticon (
+ E_EMOTICON_CHOOSER (object),
+ g_value_get_boxed (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FACE:
+ g_value_set_boxed (
+ value, e_emoticon_chooser_get_current_emoticon (
+ E_EMOTICON_CHOOSER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_finalize (GObject *object)
+{
+ EEmoticonActionPrivate *priv;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (object);
+
+ g_list_free (priv->choosers);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_emoticon_action_parent_class)->finalize (object);
+}
+
+static void
+emoticon_action_activate (GtkAction *action)
+{
+ EEmoticonActionPrivate *priv;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+ priv->current_chooser = NULL;
+}
+
+static GtkWidget *
+emoticon_action_create_menu_item (GtkAction *action)
+{
+ GtkWidget *item;
+ GtkWidget *menu;
+
+ item = gtk_image_menu_item_new ();
+ menu = gtk_action_create_menu (action);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+ gtk_widget_show (menu);
+
+ return item;
+}
+
+static GtkWidget *
+emoticon_action_create_tool_item (GtkAction *action)
+{
+ return GTK_WIDGET (e_emoticon_tool_button_new ());
+}
+
+static void
+emoticon_action_connect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+ EEmoticonActionPrivate *priv;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+ if (!E_IS_EMOTICON_CHOOSER (proxy))
+ goto chainup;
+
+ if (g_list_find (priv->choosers, proxy) != NULL)
+ goto chainup;
+
+ g_signal_connect_swapped (
+ proxy, "item-activated",
+ G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+chainup:
+ /* Chain up to parent's connect_proxy() method. */
+ GTK_ACTION_CLASS (e_emoticon_action_parent_class)->
+ connect_proxy (action, proxy);
+}
+
+static void
+emoticon_action_disconnect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+ EEmoticonActionPrivate *priv;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+ priv->choosers = g_list_remove (priv->choosers, proxy);
+
+ /* Chain up to parent's disconnect_proxy() method. */
+ GTK_ACTION_CLASS (e_emoticon_action_parent_class)->
+ disconnect_proxy (action, proxy);
+}
+
+static GtkWidget *
+emoticon_action_create_menu (GtkAction *action)
+{
+ EEmoticonActionPrivate *priv;
+ GtkWidget *widget;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+ widget = e_emoticon_chooser_menu_new ();
+
+ g_signal_connect_swapped (
+ widget, "item-activated",
+ G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+ priv->choosers = g_list_prepend (priv->choosers, widget);
+
+ return widget;
+}
+
+static EEmoticon *
+emoticon_action_get_current_emoticon (EEmoticonChooser *chooser)
+{
+ EEmoticonActionPrivate *priv;
+ EEmoticon *emoticon = NULL;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (chooser);
+
+ if (priv->current_chooser != NULL)
+ emoticon = e_emoticon_chooser_get_current_emoticon (
+ priv->current_chooser);
+
+ return emoticon;
+}
+
+static void
+emoticon_action_set_current_emoticon (EEmoticonChooser *chooser,
+ EEmoticon *emoticon)
+{
+ EEmoticonActionPrivate *priv;
+ GList *iter;
+
+ priv = E_EMOTICON_ACTION_GET_PRIVATE (chooser);
+
+ for (iter = priv->choosers; iter != NULL; iter = iter->next) {
+ EEmoticonChooser *proxy_chooser = iter->data;
+
+ e_emoticon_chooser_set_current_emoticon (proxy_chooser, emoticon);
+ }
+}
+
+static void
+e_emoticon_action_class_init (EEmoticonActionClass *class)
+{
+ GObjectClass *object_class;
+ GtkActionClass *action_class;
+
+ g_type_class_add_private (class, sizeof (EEmoticonAction));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = emoticon_action_set_property;
+ object_class->get_property = emoticon_action_get_property;
+ object_class->finalize = emoticon_action_finalize;
+
+ action_class = GTK_ACTION_CLASS (class);
+ action_class->activate = emoticon_action_activate;
+ action_class->create_menu_item = emoticon_action_create_menu_item;
+ action_class->create_tool_item = emoticon_action_create_tool_item;
+ action_class->connect_proxy = emoticon_action_connect_proxy;
+ action_class->disconnect_proxy = emoticon_action_disconnect_proxy;
+ action_class->create_menu = emoticon_action_create_menu;
+
+ g_object_class_override_property (
+ object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+e_emoticon_action_interface_init (EEmoticonChooserInterface *interface)
+{
+ interface->get_current_emoticon = emoticon_action_get_current_emoticon;
+ interface->set_current_emoticon = emoticon_action_set_current_emoticon;
+}
+
+static void
+e_emoticon_action_init (EEmoticonAction *action)
+{
+ action->priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+}
+
+GtkAction *
+e_emoticon_action_new (const gchar *name,
+ const gchar *label,
+ const gchar *tooltip,
+ const gchar *stock_id)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_object_new (
+ E_TYPE_EMOTICON_ACTION, "name", name, "label", label,
+ "tooltip", tooltip, "stock-id", stock_id, NULL);
+}
diff --git a/e-util/e-emoticon-action.h b/e-util/e-emoticon-action.h
new file mode 100644
index 0000000000..0e450e8750
--- /dev/null
+++ b/e-util/e-emoticon-action.h
@@ -0,0 +1,73 @@
+/*
+ * e-emoticon-action.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_ACTION_H
+#define E_EMOTICON_ACTION_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_ACTION \
+ (e_emoticon_action_get_type ())
+#define E_EMOTICON_ACTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonAction))
+#define E_EMOTICON_ACTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+#define E_IS_EMOTICON_ACTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_EMOTICON_ACTION))
+#define E_IS_EMOTICON_ACTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_EMOTICON_ACTION))
+#define E_EMOTICON_ACTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonAction EEmoticonAction;
+typedef struct _EEmoticonActionClass EEmoticonActionClass;
+typedef struct _EEmoticonActionPrivate EEmoticonActionPrivate;
+
+struct _EEmoticonAction {
+ GtkAction parent;
+ EEmoticonActionPrivate *priv;
+};
+
+struct _EEmoticonActionClass {
+ GtkActionClass parent_class;
+};
+
+GType e_emoticon_action_get_type (void) G_GNUC_CONST;
+GtkAction * e_emoticon_action_new (const gchar *name,
+ const gchar *label,
+ const gchar *tooltip,
+ const gchar *stock_id);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_ACTION_H */
diff --git a/e-util/e-emoticon-chooser-menu.c b/e-util/e-emoticon-chooser-menu.c
new file mode 100644
index 0000000000..f2ed3376cf
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.c
@@ -0,0 +1,184 @@
+/*
+ * e-emoticon-chooser-menu.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+ PROP_0,
+ PROP_CURRENT_FACE
+};
+
+/* Forward Declarations */
+static void e_emoticon_chooser_menu_interface_init
+ (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EEmoticonChooserMenu,
+ e_emoticon_chooser_menu,
+ GTK_TYPE_MENU,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EMOTICON_CHOOSER,
+ e_emoticon_chooser_menu_interface_init))
+
+static void
+emoticon_chooser_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FACE:
+ e_emoticon_chooser_set_current_emoticon (
+ E_EMOTICON_CHOOSER (object),
+ g_value_get_boxed (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_chooser_menu_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FACE:
+ g_value_set_boxed (
+ value,
+ e_emoticon_chooser_get_current_emoticon (
+ E_EMOTICON_CHOOSER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static EEmoticon *
+emoticon_chooser_menu_get_current_emoticon (EEmoticonChooser *chooser)
+{
+ GtkWidget *item;
+
+ item = gtk_menu_get_active (GTK_MENU (chooser));
+ if (item == NULL)
+ return NULL;
+
+ return g_object_get_data (G_OBJECT (item), "emoticon");
+}
+
+static void
+emoticon_chooser_menu_set_current_emoticon (EEmoticonChooser *chooser,
+ EEmoticon *emoticon)
+{
+ GList *list, *iter;
+
+ list = gtk_container_get_children (GTK_CONTAINER (chooser));
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ GtkWidget *item = iter->data;
+ EEmoticon *candidate;
+
+ candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+ if (candidate == NULL)
+ continue;
+
+ if (e_emoticon_equal (emoticon, candidate)) {
+ gtk_menu_shell_activate_item (
+ GTK_MENU_SHELL (chooser), item, TRUE);
+ break;
+ }
+ }
+
+ g_list_free (list);
+}
+
+static void
+e_emoticon_chooser_menu_class_init (EEmoticonChooserMenuClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = emoticon_chooser_menu_set_property;
+ object_class->get_property = emoticon_chooser_menu_get_property;
+
+ g_object_class_override_property (
+ object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+e_emoticon_chooser_menu_interface_init (EEmoticonChooserInterface *interface)
+{
+ interface->get_current_emoticon =
+ emoticon_chooser_menu_get_current_emoticon;
+ interface->set_current_emoticon =
+ emoticon_chooser_menu_set_current_emoticon;
+}
+
+static void
+e_emoticon_chooser_menu_init (EEmoticonChooserMenu *chooser_menu)
+{
+ EEmoticonChooser *chooser;
+ GList *list, *iter;
+
+ chooser = E_EMOTICON_CHOOSER (chooser_menu);
+ list = e_emoticon_chooser_get_items ();
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ EEmoticon *emoticon = iter->data;
+ GtkWidget *item;
+
+ /* To keep translated strings in subclasses */
+ item = gtk_image_menu_item_new_with_mnemonic (_(emoticon->label));
+ gtk_image_menu_item_set_image (
+ GTK_IMAGE_MENU_ITEM (item),
+ gtk_image_new_from_icon_name (
+ emoticon->icon_name, GTK_ICON_SIZE_MENU));
+ gtk_widget_show (item);
+
+ g_object_set_data_full (
+ G_OBJECT (item), "emoticon",
+ e_emoticon_copy (emoticon),
+ (GDestroyNotify) e_emoticon_free);
+
+ g_signal_connect_swapped (
+ item, "activate",
+ G_CALLBACK (e_emoticon_chooser_item_activated),
+ chooser);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item);
+ }
+
+ g_list_free (list);
+}
+
+GtkWidget *
+e_emoticon_chooser_menu_new (void)
+{
+ return g_object_new (E_TYPE_EMOTICON_CHOOSER_MENU, NULL);
+}
diff --git a/e-util/e-emoticon-chooser-menu.h b/e-util/e-emoticon-chooser-menu.h
new file mode 100644
index 0000000000..d4f99543bf
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.h
@@ -0,0 +1,70 @@
+/*
+ * e-emoticon-chooser-menu.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_MENU_H
+#define E_EMOTICON_CHOOSER_MENU_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER_MENU \
+ (e_emoticon_chooser_menu_get_type ())
+#define E_EMOTICON_CHOOSER_MENU(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenu))
+#define E_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+#define E_IS_EMOTICON_CHOOSER_MENU(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_IS_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_EMOTICON_CHOOSER_MENU_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooserMenu EEmoticonChooserMenu;
+typedef struct _EEmoticonChooserMenuClass EEmoticonChooserMenuClass;
+typedef struct _EEmoticonChooserMenuPrivate EEmoticonChooserMenuPrivate;
+
+struct _EEmoticonChooserMenu {
+ GtkMenu parent;
+};
+
+struct _EEmoticonChooserMenuClass {
+ GtkMenuClass parent_class;
+};
+
+GType e_emoticon_chooser_menu_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_emoticon_chooser_menu_new (void);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_MENU_H */
diff --git a/e-util/e-emoticon-chooser.c b/e-util/e-emoticon-chooser.c
new file mode 100644
index 0000000000..44ce06ba55
--- /dev/null
+++ b/e-util/e-emoticon-chooser.c
@@ -0,0 +1,178 @@
+/*
+ * e-emoticon-chooser.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+/* Constant version of EEMoticon. */
+typedef struct {
+ const gchar *label;
+ const gchar *icon_name;
+ const gchar *text_face;
+} ConstantEmoticon;
+
+static ConstantEmoticon available_emoticons[] = {
+ /* Translators: :-) */
+ { N_("_Smile"), "face-smile", ":-)" },
+ /* Translators: :-( */
+ { N_("S_ad"), "face-sad", ":-(" },
+ /* Translators: ;-) */
+ { N_("_Wink"), "face-wink", ";-)" },
+ /* Translators: :-P */
+ { N_("Ton_gue"), "face-raspberry", ":-P" },
+ /* Translators: :-)) */
+ { N_("Laug_h"), "face-laugh", ":-))" },
+ /* Translators: :-| */
+ { N_("_Plain"), "face-plain", ":-|" },
+ /* Translators: :-! */
+ { N_("Smi_rk"), "face-smirk", ":-!" },
+ /* Translators: :"-) */
+ { N_("_Embarrassed"), "face-embarrassed", ":\"-)" },
+ /* Translators: :-D */
+ { N_("_Big Smile"), "face-smile-big", ":-D" },
+ /* Translators: :-/ */
+ { N_("Uncer_tain"), "face-uncertain", ":-/" },
+ /* Translators: :-O */
+ { N_("S_urprise"), "face-surprise", ":-O" },
+ /* Translators: :-S */
+ { N_("W_orried"), "face-worried", ":-S" },
+ /* Translators: :-* */
+ { N_("_Kiss"), "face-kiss", ":-*" },
+ /* Translators: X-( */
+ { N_("A_ngry"), "face-angry", "X-(" },
+ /* Translators: B-) */
+ { N_("_Cool"), "face-cool", "B-)" },
+ /* Translators: O:-) */
+ { N_("Ange_l"), "face-angel", "O:-)" },
+ /* Translators: :'( */
+ { N_("Cr_ying"), "face-crying", ":'(" },
+ /* Translators: :-Q */
+ { N_("S_ick"), "face-sick", ":-Q" },
+ /* Translators: |-) */
+ { N_("Tire_d"), "face-tired", "|-)" },
+ /* Translators: >:-) */
+ { N_("De_vilish"), "face-devilish", ">:-)" },
+ /* Translators: :-(|) */
+ { N_("_Monkey"), "face-monkey", ":-(|)" }
+};
+
+enum {
+ ITEM_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_INTERFACE (
+ EEmoticonChooser,
+ e_emoticon_chooser,
+ G_TYPE_OBJECT)
+
+static void
+e_emoticon_chooser_default_init (EEmoticonChooserInterface *interface)
+{
+ g_object_interface_install_property (
+ interface,
+ g_param_spec_boxed (
+ "current-emoticon",
+ "Current Emoticon",
+ "Currently selected emoticon",
+ E_TYPE_EMOTICON,
+ G_PARAM_READWRITE));
+
+ signals[ITEM_ACTIVATED] = g_signal_new (
+ "item-activated",
+ G_TYPE_FROM_INTERFACE (interface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EEmoticonChooserInterface, item_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+EEmoticon *
+e_emoticon_chooser_get_current_emoticon (EEmoticonChooser *chooser)
+{
+ EEmoticonChooserInterface *interface;
+
+ g_return_val_if_fail (E_IS_EMOTICON_CHOOSER (chooser), NULL);
+
+ interface = E_EMOTICON_CHOOSER_GET_INTERFACE (chooser);
+ g_return_val_if_fail (interface->get_current_emoticon != NULL, NULL);
+
+ return interface->get_current_emoticon (chooser);
+}
+
+void
+e_emoticon_chooser_set_current_emoticon (EEmoticonChooser *chooser,
+ EEmoticon *emoticon)
+{
+ EEmoticonChooserInterface *interface;
+
+ g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+ interface = E_EMOTICON_CHOOSER_GET_INTERFACE (chooser);
+ g_return_if_fail (interface->set_current_emoticon != NULL);
+
+ interface->set_current_emoticon (chooser, emoticon);
+}
+
+void
+e_emoticon_chooser_item_activated (EEmoticonChooser *chooser)
+{
+ g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+ g_signal_emit (chooser, signals[ITEM_ACTIVATED], 0);
+}
+
+GList *
+e_emoticon_chooser_get_items (void)
+{
+ GList *list = NULL;
+ gint ii;
+
+ for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++)
+ list = g_list_prepend (list, &available_emoticons[ii]);
+
+ return g_list_reverse (list);
+}
+
+const EEmoticon *
+e_emoticon_chooser_lookup_emoticon (const gchar *icon_name)
+{
+ gint ii;
+
+ g_return_val_if_fail (icon_name && *icon_name, NULL);
+
+ for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++) {
+ if (strcmp (available_emoticons[ii].icon_name, icon_name) == 0) {
+ return (const EEmoticon *) &available_emoticons[ii];
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/e-util/e-emoticon-chooser.h b/e-util/e-emoticon-chooser.h
new file mode 100644
index 0000000000..14e899fd4e
--- /dev/null
+++ b/e-util/e-emoticon-chooser.h
@@ -0,0 +1,77 @@
+/*
+ * e-emoticon-chooser.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_H
+#define E_EMOTICON_CHOOSER_H
+
+#include <e-util/e-emoticon.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER \
+ (e_emoticon_chooser_get_type ())
+#define E_EMOTICON_CHOOSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooser))
+#define E_IS_EMOTICON_CHOOSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_EMOTICON_CHOOSER))
+#define E_EMOTICON_CHOOSER_GET_INTERFACE(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE \
+ ((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooserInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooser EEmoticonChooser;
+typedef struct _EEmoticonChooserInterface EEmoticonChooserInterface;
+
+struct _EEmoticonChooserInterface {
+ GTypeInterface parent_interface;
+
+ /* Methods */
+ EEmoticon * (*get_current_emoticon) (EEmoticonChooser *chooser);
+ void (*set_current_emoticon) (EEmoticonChooser *chooser,
+ EEmoticon *emoticon);
+
+ /* Signals */
+ void (*item_activated) (EEmoticonChooser *chooser);
+};
+
+GType e_emoticon_chooser_get_type (void) G_GNUC_CONST;
+EEmoticon * e_emoticon_chooser_get_current_emoticon
+ (EEmoticonChooser *chooser);
+void e_emoticon_chooser_set_current_emoticon
+ (EEmoticonChooser *chooser,
+ EEmoticon *emoticon);
+void e_emoticon_chooser_item_activated
+ (EEmoticonChooser *chooser);
+
+GList * e_emoticon_chooser_get_items (void);
+const EEmoticon *
+ e_emoticon_chooser_lookup_emoticon
+ (const gchar *icon_name);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_H */
diff --git a/e-util/e-emoticon-tool-button.c b/e-util/e-emoticon-tool-button.c
new file mode 100644
index 0000000000..54f99c94b0
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.c
@@ -0,0 +1,695 @@
+/*
+ * e-emoticon-tool-button.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-tool-button.h"
+
+/* XXX The "button" aspects of this widget are based heavily on the
+ * GtkComboBox tree-view implementation. Consider splitting it
+ * into a reusable "button-with-an-empty-window" widget. */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "e-emoticon-chooser.h"
+
+#define E_EMOTICON_TOOL_BUTTON_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonPrivate))
+
+/* XXX Should calculate this dynamically. */
+#define NUM_ROWS 7
+#define NUM_COLS 3
+
+enum {
+ PROP_0,
+ PROP_CURRENT_EMOTICON,
+ PROP_POPUP_SHOWN
+};
+
+enum {
+ POPUP,
+ POPDOWN,
+ LAST_SIGNAL
+};
+
+struct _EEmoticonToolButtonPrivate {
+ GtkWidget *active_button; /* not referenced */
+ GtkWidget *table;
+ GtkWidget *window;
+
+ guint popup_shown : 1;
+ guint popup_in_progress : 1;
+ GdkDevice *grab_keyboard;
+ GdkDevice *grab_mouse;
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void e_emoticon_tool_button_interface_init
+ (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EEmoticonToolButton,
+ e_emoticon_tool_button,
+ GTK_TYPE_TOGGLE_TOOL_BUTTON,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EMOTICON_CHOOSER,
+ e_emoticon_tool_button_interface_init))
+
+/* XXX Copied from _gtk_toolbar_elide_underscores() */
+static gchar *
+emoticon_tool_button_elide_underscores (const gchar *original)
+{
+ gchar *q, *result;
+ const gchar *p, *end;
+ gsize len;
+ gboolean last_underscore;
+
+ if (!original)
+ return NULL;
+
+ len = strlen (original);
+ q = result = g_malloc (len + 1);
+ last_underscore = FALSE;
+
+ end = original + len;
+ for (p = original; p < end; p++) {
+ if (!last_underscore && *p == '_')
+ last_underscore = TRUE;
+ else {
+ last_underscore = FALSE;
+ if (original + 2 <= p && p + 1 <= end &&
+ p[-2] == '(' && p[-1] == '_' &&
+ p[0] != '_' && p[1] == ')') {
+ q--;
+ *q = '\0';
+ p++;
+ } else
+ *q++ = *p;
+ }
+ }
+
+ if (last_underscore)
+ *q++ = '_';
+
+ *q = '\0';
+
+ return result;
+}
+
+static void
+emoticon_tool_button_reposition_window (EEmoticonToolButton *button)
+{
+ GdkScreen *screen;
+ GdkWindow *window;
+ GdkRectangle monitor;
+ GtkAllocation allocation;
+ gint monitor_num;
+ gint x, y, width, height;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (button));
+ window = gtk_widget_get_window (GTK_WIDGET (button));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (window, &x, &y);
+
+ if (!gtk_widget_get_has_window (GTK_WIDGET (button))) {
+ gtk_widget_get_allocation (GTK_WIDGET (button), &allocation);
+ x += allocation.x;
+ y += allocation.y;
+ }
+
+ gtk_widget_get_allocation (button->priv->window, &allocation);
+ width = allocation.width;
+ height = allocation.height;
+
+ x = CLAMP (x, monitor.x, monitor.x + monitor.width - width);
+ y = CLAMP (y, monitor.y, monitor.y + monitor.height - height);
+
+ gtk_window_move (GTK_WINDOW (button->priv->window), x, y);
+}
+
+static void
+emoticon_tool_button_emoticon_clicked_cb (EEmoticonToolButton *button,
+ GtkWidget *emoticon_button)
+{
+ button->priv->active_button = emoticon_button;
+ e_emoticon_tool_button_popdown (button);
+}
+
+static gboolean
+emoticon_tool_button_emoticon_release_event_cb (EEmoticonToolButton *button,
+ GdkEventButton *event,
+ GtkButton *emoticon_button)
+{
+ GtkStateType state;
+
+ state = gtk_widget_get_state (GTK_WIDGET (button));
+
+ if (state != GTK_STATE_NORMAL)
+ gtk_button_clicked (emoticon_button);
+
+ return FALSE;
+}
+
+static gboolean
+emoticon_tool_button_button_release_event_cb (EEmoticonToolButton *button,
+ GdkEventButton *event)
+{
+ GtkToggleToolButton *tool_button;
+ GtkWidget *event_widget;
+ gboolean popup_in_progress;
+
+ tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+ event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+ popup_in_progress = button->priv->popup_in_progress;
+ button->priv->popup_in_progress = FALSE;
+
+ if (event_widget != GTK_WIDGET (button))
+ goto popdown;
+
+ if (popup_in_progress)
+ return FALSE;
+
+ if (gtk_toggle_tool_button_get_active (tool_button))
+ goto popdown;
+
+ return FALSE;
+
+popdown:
+ e_emoticon_tool_button_popdown (button);
+
+ return TRUE;
+}
+
+static void
+emoticon_tool_button_child_show_cb (EEmoticonToolButton *button)
+{
+ button->priv->popup_shown = TRUE;
+ g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static void
+emoticon_tool_button_child_hide_cb (EEmoticonToolButton *button)
+{
+ button->priv->popup_shown = FALSE;
+ g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static gboolean
+emoticon_tool_button_child_key_press_event_cb (EEmoticonToolButton *button,
+ GdkEventKey *event)
+{
+ GtkWidget *window = button->priv->window;
+
+ if (!gtk_bindings_activate_event (G_OBJECT (window), event))
+ gtk_bindings_activate_event (G_OBJECT (button), event);
+
+ return TRUE;
+}
+
+static void
+emoticon_tool_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_EMOTICON:
+ e_emoticon_chooser_set_current_emoticon (
+ E_EMOTICON_CHOOSER (object),
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_POPUP_SHOWN:
+ if (g_value_get_boolean (value))
+ e_emoticon_tool_button_popup (
+ E_EMOTICON_TOOL_BUTTON (object));
+ else
+ e_emoticon_tool_button_popdown (
+ E_EMOTICON_TOOL_BUTTON (object));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EEmoticonToolButtonPrivate *priv;
+
+ priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (object);
+
+ switch (property_id) {
+ case PROP_CURRENT_EMOTICON:
+ g_value_set_boxed (
+ value,
+ e_emoticon_chooser_get_current_emoticon (
+ E_EMOTICON_CHOOSER (object)));
+ return;
+
+ case PROP_POPUP_SHOWN:
+ g_value_set_boolean (value, priv->popup_shown);
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_dispose (GObject *object)
+{
+ EEmoticonToolButtonPrivate *priv;
+
+ priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (object);
+
+ if (priv->window != NULL) {
+ gtk_widget_destroy (priv->window);
+ priv->window = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_emoticon_tool_button_parent_class)->dispose (object);
+}
+
+static gboolean
+emoticon_tool_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EEmoticonToolButton *button;
+ GtkToggleToolButton *toggle_button;
+ GtkWidget *event_widget;
+
+ button = E_EMOTICON_TOOL_BUTTON (widget);
+
+ event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+ if (event_widget == button->priv->window)
+ return TRUE;
+
+ if (event_widget != widget)
+ return FALSE;
+
+ toggle_button = GTK_TOGGLE_TOOL_BUTTON (widget);
+ if (gtk_toggle_tool_button_get_active (toggle_button))
+ return FALSE;
+
+ e_emoticon_tool_button_popup (button);
+
+ button->priv->popup_in_progress = TRUE;
+
+ return TRUE;
+}
+
+static void
+emoticon_tool_button_toggled (GtkToggleToolButton *button)
+{
+ if (gtk_toggle_tool_button_get_active (button))
+ e_emoticon_tool_button_popup (
+ E_EMOTICON_TOOL_BUTTON (button));
+ else
+ e_emoticon_tool_button_popdown (
+ E_EMOTICON_TOOL_BUTTON (button));
+}
+
+static void
+emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+ GtkToggleToolButton *tool_button;
+ GdkWindow *window;
+ gboolean grab_status;
+ GdkDevice *device, *mouse, *keyboard;
+ guint32 activate_time;
+
+ device = gtk_get_current_event_device ();
+ g_return_if_fail (device != NULL);
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+ return;
+
+ if (button->priv->popup_shown)
+ return;
+
+ activate_time = gtk_get_current_event_time ();
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) {
+ keyboard = device;
+ mouse = gdk_device_get_associated_device (device);
+ } else {
+ keyboard = gdk_device_get_associated_device (device);
+ mouse = device;
+ }
+
+ /* Position the window over the button. */
+ emoticon_tool_button_reposition_window (button);
+
+ /* Show the pop-up. */
+ gtk_widget_show (button->priv->window);
+ gtk_widget_grab_focus (button->priv->window);
+
+ /* Activate the tool button. */
+ tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+ gtk_toggle_tool_button_set_active (tool_button, TRUE);
+
+ /* Try to grab the pointer and keyboard. */
+ window = gtk_widget_get_window (button->priv->window);
+ grab_status = !keyboard ||
+ gdk_device_grab (
+ keyboard, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, activate_time) == GDK_GRAB_SUCCESS;
+ if (grab_status) {
+ grab_status = !mouse ||
+ gdk_device_grab (mouse, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+ NULL, activate_time) == GDK_GRAB_SUCCESS;
+ if (!grab_status && keyboard)
+ gdk_device_ungrab (keyboard, activate_time);
+ }
+
+ if (grab_status) {
+ gtk_device_grab_add (button->priv->window, mouse, TRUE);
+ button->priv->grab_keyboard = keyboard;
+ button->priv->grab_mouse = mouse;
+ } else {
+ gtk_widget_hide (button->priv->window);
+ }
+}
+
+static void
+emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+ GtkToggleToolButton *tool_button;
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+ return;
+
+ if (!button->priv->popup_shown)
+ return;
+
+ /* Hide the pop-up. */
+ gtk_device_grab_remove (button->priv->window, button->priv->grab_mouse);
+ gtk_widget_hide (button->priv->window);
+
+ /* Deactivate the tool button. */
+ tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+ gtk_toggle_tool_button_set_active (tool_button, FALSE);
+
+ if (button->priv->grab_keyboard)
+ gdk_device_ungrab (button->priv->grab_keyboard, GDK_CURRENT_TIME);
+ if (button->priv->grab_mouse)
+ gdk_device_ungrab (button->priv->grab_mouse, GDK_CURRENT_TIME);
+
+ button->priv->grab_keyboard = NULL;
+ button->priv->grab_mouse = NULL;
+}
+
+static EEmoticon *
+emoticon_tool_button_get_current_emoticon (EEmoticonChooser *chooser)
+{
+ EEmoticonToolButtonPrivate *priv;
+
+ priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+ if (priv->active_button == NULL)
+ return NULL;
+
+ return g_object_get_data (G_OBJECT (priv->active_button), "emoticon");
+}
+
+static void
+emoticon_tool_button_set_current_emoticon (EEmoticonChooser *chooser,
+ EEmoticon *emoticon)
+{
+ EEmoticonToolButtonPrivate *priv;
+ GList *list, *iter;
+
+ priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+ list = gtk_container_get_children (GTK_CONTAINER (priv->table));
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ GtkWidget *item = iter->data;
+ EEmoticon *candidate;
+
+ candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+ if (candidate == NULL)
+ continue;
+
+ if (e_emoticon_equal (emoticon, candidate)) {
+ gtk_button_clicked (GTK_BUTTON (item));
+ break;
+ }
+ }
+
+ g_list_free (list);
+}
+
+static void
+e_emoticon_tool_button_class_init (EEmoticonToolButtonClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkToggleToolButtonClass *toggle_tool_button_class;
+
+ g_type_class_add_private (class, sizeof (EEmoticonToolButtonPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = emoticon_tool_button_set_property;
+ object_class->get_property = emoticon_tool_button_get_property;
+ object_class->dispose = emoticon_tool_button_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->button_press_event = emoticon_tool_button_press_event;
+
+ toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (class);
+ toggle_tool_button_class->toggled = emoticon_tool_button_toggled;
+
+ class->popup = emoticon_tool_button_popup;
+ class->popdown = emoticon_tool_button_popdown;
+
+ g_object_class_override_property (
+ object_class, PROP_CURRENT_EMOTICON, "current-emoticon");
+
+ g_object_class_install_property (
+ object_class,
+ PROP_POPUP_SHOWN,
+ g_param_spec_boolean (
+ "popup-shown",
+ "Popup Shown",
+ "Whether the button's dropdown is shown",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ signals[POPUP] = g_signal_new (
+ "popup",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EEmoticonToolButtonClass, popup),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[POPDOWN] = g_signal_new (
+ "popdown",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EEmoticonToolButtonClass, popdown),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+ gtk_binding_entry_add_signal (
+ gtk_binding_set_by_class (class),
+ GDK_KEY_Escape, 0, "popdown", 0);
+}
+
+static void
+e_emoticon_tool_button_interface_init (EEmoticonChooserInterface *interface)
+{
+ interface->get_current_emoticon =
+ emoticon_tool_button_get_current_emoticon;
+ interface->set_current_emoticon =
+ emoticon_tool_button_set_current_emoticon;
+}
+
+static void
+e_emoticon_tool_button_init (EEmoticonToolButton *button)
+{
+ EEmoticonChooser *chooser;
+ GtkWidget *toplevel;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkWidget *window;
+ GList *list, *iter;
+ gint ii;
+
+ button->priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (button);
+
+ /* Build the pop-up window. */
+
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+ gtk_window_set_type_hint (
+ GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_COMBO);
+ button->priv->window = g_object_ref_sink (window);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ if (GTK_IS_WINDOW (toplevel)) {
+ gtk_window_group_add_window (
+ gtk_window_get_group (GTK_WINDOW (toplevel)),
+ GTK_WINDOW (window));
+ gtk_window_set_transient_for (
+ GTK_WINDOW (window), GTK_WINDOW (toplevel));
+ }
+
+ g_signal_connect_swapped (
+ window, "show",
+ G_CALLBACK (emoticon_tool_button_child_show_cb), button);
+ g_signal_connect_swapped (
+ window, "hide",
+ G_CALLBACK (emoticon_tool_button_child_hide_cb), button);
+ g_signal_connect_swapped (
+ window, "button-release-event",
+ G_CALLBACK (emoticon_tool_button_button_release_event_cb),
+ button);
+ g_signal_connect_swapped (
+ window, "key-press-event",
+ G_CALLBACK (emoticon_tool_button_child_key_press_event_cb),
+ button);
+
+ /* Build the pop-up window contents. */
+
+ widget = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (window), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_table_new (NUM_ROWS, NUM_COLS, TRUE);
+ gtk_table_set_row_spacings (GTK_TABLE (widget), 0);
+ gtk_table_set_col_spacings (GTK_TABLE (widget), 0);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ button->priv->table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ chooser = E_EMOTICON_CHOOSER (button);
+ list = e_emoticon_chooser_get_items ();
+ g_assert (g_list_length (list) <= NUM_ROWS * NUM_COLS);
+
+ for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+ EEmoticon *emoticon = iter->data;
+ guint left = ii % NUM_COLS;
+ guint top = ii / NUM_COLS;
+ gchar *tooltip;
+
+ tooltip = emoticon_tool_button_elide_underscores (
+ gettext (emoticon->label));
+
+ widget = gtk_button_new ();
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_icon_name (
+ emoticon->icon_name, GTK_ICON_SIZE_BUTTON));
+ gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text (widget, tooltip);
+ gtk_widget_show (widget);
+
+ g_object_set_data_full (
+ G_OBJECT (widget), "emoticon",
+ e_emoticon_copy (emoticon),
+ (GDestroyNotify) e_emoticon_free);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (emoticon_tool_button_emoticon_clicked_cb),
+ button);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (e_emoticon_chooser_item_activated),
+ chooser);
+
+ g_signal_connect_swapped (
+ widget, "button-release-event",
+ G_CALLBACK (emoticon_tool_button_emoticon_release_event_cb),
+ button);
+
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ left, left + 1, top, top + 1, 0, 0, 0, 0);
+
+ g_free (tooltip);
+ }
+
+ g_list_free (list);
+}
+
+GtkToolItem *
+e_emoticon_tool_button_new (void)
+{
+ return g_object_new (E_TYPE_EMOTICON_TOOL_BUTTON, NULL);
+}
+
+void
+e_emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+ g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+ g_signal_emit (button, signals[POPUP], 0);
+}
+
+void
+e_emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+ g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+ g_signal_emit (button, signals[POPDOWN], 0);
+}
diff --git a/e-util/e-emoticon-tool-button.h b/e-util/e-emoticon-tool-button.h
new file mode 100644
index 0000000000..fc7dc8ef64
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.h
@@ -0,0 +1,75 @@
+/*
+ * e-emoticon-tool-button.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_TOOL_BUTTON_H
+#define E_EMOTICON_TOOL_BUTTON_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_TOOL_BUTTON \
+ (e_emoticon_tool_button_get_type ())
+#define E_EMOTICON_TOOL_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButton))
+#define E_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+#define E_IS_EMOTICON_TOOL_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_IS_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_EMOTICON_TOOL_BUTTON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonToolButton EEmoticonToolButton;
+typedef struct _EEmoticonToolButtonClass EEmoticonToolButtonClass;
+typedef struct _EEmoticonToolButtonPrivate EEmoticonToolButtonPrivate;
+
+struct _EEmoticonToolButton {
+ GtkToggleToolButton parent;
+ EEmoticonToolButtonPrivate *priv;
+};
+
+struct _EEmoticonToolButtonClass {
+ GtkToggleToolButtonClass parent_class;
+
+ void (*popup) (EEmoticonToolButton *button);
+ void (*popdown) (EEmoticonToolButton *button);
+};
+
+GType e_emoticon_tool_button_get_type (void) G_GNUC_CONST;
+GtkToolItem * e_emoticon_tool_button_new (void);
+void e_emoticon_tool_button_popup (EEmoticonToolButton *button);
+void e_emoticon_tool_button_popdown (EEmoticonToolButton *button);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_TOOL_BUTTON_H */
diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c
new file mode 100644
index 0000000000..c543e52417
--- /dev/null
+++ b/e-util/e-emoticon.c
@@ -0,0 +1,118 @@
+/*
+ * e-emoticon.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon.h"
+
+#include <gtk/gtk.h>
+
+static EEmoticon *
+emoticon_copy (EEmoticon *emoticon)
+{
+ EEmoticon *copy;
+
+ copy = g_slice_new (EEmoticon);
+ copy->label = g_strdup (emoticon->label);
+ copy->icon_name = g_strdup (emoticon->icon_name);
+ copy->text_face = g_strdup (emoticon->text_face);
+
+ return copy;
+}
+
+static void
+emoticon_free (EEmoticon *emoticon)
+{
+ g_free (emoticon->label);
+ g_free (emoticon->icon_name);
+ g_free (emoticon->text_face);
+ g_slice_free (EEmoticon, emoticon);
+}
+
+GType
+e_emoticon_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ type = g_boxed_type_register_static (
+ "EEmoticon",
+ (GBoxedCopyFunc) emoticon_copy,
+ (GBoxedFreeFunc) emoticon_free);
+
+ return type;
+}
+
+gboolean
+e_emoticon_equal (EEmoticon *emoticon_a,
+ EEmoticon *emoticon_b)
+{
+ if (((emoticon_a == NULL) && (emoticon_b != NULL)) ||
+ ((emoticon_a != NULL) && (emoticon_b == NULL)))
+ return FALSE;
+
+ if (emoticon_a == emoticon_b)
+ return TRUE;
+
+ if (g_strcmp0 (emoticon_a->label, emoticon_b->label) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (emoticon_a->icon_name, emoticon_b->icon_name) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (emoticon_a->text_face, emoticon_b->text_face) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+EEmoticon *
+e_emoticon_copy (EEmoticon *emoticon)
+{
+ return g_boxed_copy (E_TYPE_EMOTICON, emoticon);
+}
+
+void
+e_emoticon_free (EEmoticon *emoticon)
+{
+ g_boxed_free (E_TYPE_EMOTICON, emoticon);
+}
+
+gchar *
+e_emoticon_get_uri (EEmoticon *emoticon)
+{
+ GtkIconInfo *icon_info;
+ GtkIconTheme *icon_theme;
+ const gchar *filename;
+ gchar *uri = NULL;
+
+ icon_theme = gtk_icon_theme_get_default ();
+ icon_info = gtk_icon_theme_lookup_icon (
+ icon_theme, emoticon->icon_name, 16, 0);
+ g_return_val_if_fail (icon_info != NULL, NULL);
+
+ filename = gtk_icon_info_get_filename (icon_info);
+ if (filename != NULL) {
+ uri = g_filename_to_uri (filename, NULL, NULL);
+ }
+ gtk_icon_info_free (icon_info);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri;
+}
diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h
new file mode 100644
index 0000000000..66327ab788
--- /dev/null
+++ b/e-util/e-emoticon.h
@@ -0,0 +1,53 @@
+/*
+ * e-emoticon.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_H
+#define E_EMOTICON_H
+
+#include <glib-object.h>
+
+#define E_TYPE_EMOTICON \
+ (e_emoticon_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticon EEmoticon;
+
+struct _EEmoticon {
+ gchar *label;
+ gchar *icon_name;
+ gchar *text_face;
+};
+
+GType e_emoticon_get_type (void) G_GNUC_CONST;
+gboolean e_emoticon_equal (EEmoticon *emoticon_a,
+ EEmoticon *emoticon_b);
+EEmoticon * e_emoticon_copy (EEmoticon *emoticon);
+void e_emoticon_free (EEmoticon *emoticon);
+gchar * e_emoticon_get_uri (EEmoticon *emoticon);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_H */
diff --git a/e-util/e-focus-tracker.c b/e-util/e-focus-tracker.c
index 0879a16c1d..3d3daec9fb 100644
--- a/e-util/e-focus-tracker.c
+++ b/e-util/e-focus-tracker.c
@@ -28,6 +28,7 @@
#include "e-selectable.h"
#include "e-widget-undo.h"
+#include "e-html-editor-view.h"
#define E_FOCUS_TRACKER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -255,6 +256,42 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker,
}
static void
+focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
+ EHTMLEditorView *view,
+ GdkAtom *targets,
+ gint n_targets)
+{
+ GtkAction *action;
+ gboolean can_copy;
+ gboolean can_cut;
+ gboolean can_paste;
+
+ g_object_get (view,
+ "can-copy", &can_copy,
+ "can-cut", &can_cut,
+ "can-paste", &can_paste,
+ NULL);
+
+ action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+ if (action != NULL) {
+ gtk_action_set_sensitive (action, can_cut);
+ gtk_action_set_tooltip (action, _("Cut the selection"));
+ }
+
+ action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+ if (action != NULL) {
+ gtk_action_set_sensitive (action, can_copy);
+ gtk_action_set_tooltip (action, _("Copy the selection"));
+ }
+
+ action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+ if (action != NULL) {
+ gtk_action_set_sensitive (action, can_paste);
+ gtk_action_set_tooltip (action, _("Paste the clipboard"));
+ }
+}
+
+static void
focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
ESelectable *selectable,
GdkAtom *targets,
@@ -331,6 +368,11 @@ focus_tracker_targets_received_cb (GtkClipboard *clipboard,
focus_tracker, GTK_TEXT_VIEW (focus),
targets, n_targets);
+ else if (E_IS_HTML_EDITOR_VIEW (focus))
+ focus_tracker_editor_update_actions (
+ focus_tracker, E_HTML_EDITOR_VIEW (focus),
+ targets, n_targets);
+
g_object_unref (focus_tracker);
}
@@ -349,6 +391,9 @@ focus_tracker_set_focus_cb (GtkWindow *window,
if (GTK_IS_TEXT_VIEW (focus))
break;
+ if (E_IS_HTML_EDITOR_VIEW (focus))
+ break;
+
focus = gtk_widget_get_parent (focus);
}
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
new file mode 100644
index 0000000000..6db3ad48da
--- /dev/null
+++ b/e-util/e-html-editor-actions.c
@@ -0,0 +1,2028 @@
+/* e-html-editor-actions.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor.h"
+#include "e-html-editor-private.h"
+#include "e-html-editor-actions.h"
+#include "e-html-editor-utils.h"
+#include "e-emoticon-action.h"
+#include "e-emoticon-chooser.h"
+#include "e-image-chooser-dialog.h"
+#include "e-spell-checker.h"
+
+static void
+insert_html_file_ready_cb (GFile *file,
+ GAsyncResult *result,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorSelection *selection;
+ gchar *contents = NULL;
+ gsize length;
+ GError *error = NULL;
+
+ g_file_load_contents_finish (
+ file, result, &contents, &length, NULL, &error);
+ if (error != NULL) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+ 0, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE, _("Failed to insert HTML file."));
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_clear_error (&error);
+ g_object_unref (editor);
+ return;
+ }
+
+ selection = e_html_editor_view_get_selection (
+ e_html_editor_get_view (editor));
+ e_html_editor_selection_insert_html (selection, contents);
+ g_free (contents);
+
+ g_object_unref (editor);
+}
+
+static void
+insert_text_file_ready_cb (GFile *file,
+ GAsyncResult *result,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorSelection *selection;
+ gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ g_file_load_contents_finish (
+ file, result, &contents, &length, NULL, &error);
+ if (error != NULL) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+ 0, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE, _("Failed to insert text file."));
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_clear_error (&error);
+ g_object_unref (editor);
+ return;
+ }
+
+ selection = e_html_editor_view_get_selection (
+ e_html_editor_get_view (editor));
+ e_html_editor_selection_insert_text (selection, contents);
+ g_free (contents);
+
+ g_object_unref (editor);
+}
+
+static void
+editor_update_static_spell_actions (EHTMLEditor *editor)
+{
+ ESpellChecker *checker;
+ EHTMLEditorView *view;
+ guint count;
+
+ view = e_html_editor_get_view (editor);
+ checker = e_html_editor_view_get_spell_checker (view);
+
+ count = e_spell_checker_count_active_languages (checker);
+
+ gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1);
+ gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1);
+ gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0);
+
+ gtk_action_set_sensitive (ACTION (SPELL_CHECK), count > 0);
+}
+
+/*****************************************************************************
+ * Action Callbacks
+ *****************************************************************************/
+
+static void
+action_context_delete_cell_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMNode *sibling;
+ WebKitDOMElement *cell;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+ if (!cell) {
+ cell = e_html_editor_dom_node_find_parent_element (
+ editor->priv->table_cell, "TH");
+ }
+ g_return_if_fail (cell != NULL);
+
+ sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (cell));
+ if (!sibling) {
+ sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (cell));
+ }
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
+ WEBKIT_DOM_NODE (cell), NULL);
+
+ if (sibling) {
+ webkit_dom_html_table_cell_element_set_col_span (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (sibling),
+ webkit_dom_html_table_cell_element_get_col_span (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (sibling)) + 1);
+ }
+}
+
+static void
+action_context_delete_column_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *cell, *table;
+ WebKitDOMHTMLCollection *rows;
+ gulong index, length, ii;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ /* Find TD in which the selection starts */
+ cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+ if (!cell) {
+ cell = e_html_editor_dom_node_find_parent_element (
+ editor->priv->table_cell, "TH");
+ }
+ g_return_if_fail (cell != NULL);
+
+ table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+ g_return_if_fail (table != NULL);
+
+ rows = webkit_dom_html_table_element_get_rows (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+ length = webkit_dom_html_collection_get_length (rows);
+
+ index = webkit_dom_html_table_cell_element_get_cell_index (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *row;
+
+ row = webkit_dom_html_collection_item (rows, ii);
+
+ webkit_dom_html_table_row_element_delete_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL);
+ }
+}
+
+static void
+action_context_delete_row_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *row;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+ g_return_if_fail (row != NULL);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
+ WEBKIT_DOM_NODE (row), NULL);
+}
+
+static void
+action_context_delete_table_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *table;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
+ g_return_if_fail (table != NULL);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+ WEBKIT_DOM_NODE (table), NULL);
+}
+
+static void
+action_context_insert_column_after_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *cell, *row;
+ gulong index;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+ if (!cell) {
+ cell = e_html_editor_dom_node_find_parent_element (
+ editor->priv->table_cell, "TH");
+ }
+ g_return_if_fail (cell != NULL);
+
+ row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+ g_return_if_fail (row != NULL);
+
+ /* Get the first row in the table */
+ row = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_get_first_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+ index = webkit_dom_html_table_cell_element_get_cell_index (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+ while (row) {
+ webkit_dom_html_table_row_element_insert_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL);
+
+ row = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+ }
+}
+
+static void
+action_context_insert_column_before_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *cell, *row;
+ gulong index;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+ if (!cell) {
+ cell = e_html_editor_dom_node_find_parent_element (
+ editor->priv->table_cell, "TH");
+ }
+ g_return_if_fail (cell != NULL);
+
+ row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+ g_return_if_fail (row != NULL);
+
+ /* Get the first row in the table */
+ row = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_get_first_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+ index = webkit_dom_html_table_cell_element_get_cell_index (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+ while (row) {
+ webkit_dom_html_table_row_element_insert_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL);
+
+ row = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+ }
+}
+
+static void
+action_context_insert_row_above_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *row, *table;
+ WebKitDOMHTMLCollection *cells;
+ WebKitDOMHTMLElement *new_row;
+ gulong index, cell_count, ii;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+ g_return_if_fail (row != NULL);
+
+ table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+ g_return_if_fail (table != NULL);
+
+ index = webkit_dom_html_table_row_element_get_row_index (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+ new_row = webkit_dom_html_table_element_insert_row (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL);
+
+ cells = webkit_dom_html_table_row_element_get_cells (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+ cell_count = webkit_dom_html_collection_get_length (cells);
+ for (ii = 0; ii < cell_count; ii++) {
+ webkit_dom_html_table_row_element_insert_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+ }
+
+}
+
+static void
+action_context_insert_row_below_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitDOMElement *row, *table;
+ WebKitDOMHTMLCollection *cells;
+ WebKitDOMHTMLElement *new_row;
+ gulong index, cell_count, ii;
+
+ g_return_if_fail (editor->priv->table_cell != NULL);
+
+ row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+ g_return_if_fail (row != NULL);
+
+ table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+ g_return_if_fail (table != NULL);
+
+ index = webkit_dom_html_table_row_element_get_row_index (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+ new_row = webkit_dom_html_table_element_insert_row (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL);
+
+ cells = webkit_dom_html_table_row_element_get_cells (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+ cell_count = webkit_dom_html_collection_get_length (cells);
+ for (ii = 0; ii < cell_count; ii++) {
+ webkit_dom_html_table_row_element_insert_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+ }
+}
+
+static void
+action_context_remove_link_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_unlink (selection);
+}
+
+static void
+action_context_spell_add_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ ESpellChecker *spell_checker;
+ EHTMLEditorSelection *selection;
+ gchar *word;
+
+ spell_checker = e_html_editor_view_get_spell_checker (
+ editor->priv->html_editor_view);
+ selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
+
+ word = e_html_editor_selection_get_caret_word (selection);
+ if (word && *word) {
+ e_spell_checker_learn_word (spell_checker, word);
+ }
+}
+
+static void
+action_context_spell_ignore_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ ESpellChecker *spell_checker;
+ EHTMLEditorSelection *selection;
+ gchar *word;
+
+ spell_checker = e_html_editor_view_get_spell_checker (
+ editor->priv->html_editor_view);
+ selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
+
+ word = e_html_editor_selection_get_caret_word (selection);
+ if (word && *word) {
+ e_spell_checker_ignore_word (spell_checker, word);
+ }
+}
+
+static void
+action_copy_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ webkit_web_view_copy_clipboard (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_cut_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ webkit_web_view_cut_clipboard (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_indent_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ e_html_editor_selection_indent (editor->priv->selection);
+}
+
+static void
+action_insert_emoticon_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+ EEmoticon *emoticon;
+
+ emoticon = e_emoticon_chooser_get_current_emoticon (
+ E_EMOTICON_CHOOSER (action));
+ g_return_if_fail (emoticon != NULL);
+
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_insert_smiley (view, emoticon);
+}
+
+static void
+action_insert_html_file_cb (GtkToggleAction *action,
+ EHTMLEditor *editor)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Insert HTML File"), NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("HTML file"));
+ gtk_file_filter_add_mime_type (filter, "text/html");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ GFile *file;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+ /* XXX Need a way to cancel this. */
+ g_file_load_contents_async (
+ file, NULL, (GAsyncReadyCallback)
+ insert_html_file_ready_cb,
+ g_object_ref (editor));
+
+ g_object_unref (file);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_image_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ GtkWidget *dialog;
+
+ dialog = e_image_chooser_dialog_new (_("Insert Image"), NULL);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ gchar *uri;
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ e_html_editor_selection_insert_image (selection, uri);
+
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_link_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->link_dialog == NULL)
+ editor->priv->link_dialog =
+ e_html_editor_link_dialog_new (editor);
+
+ gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
+}
+
+static void
+action_insert_rule_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->hrule_dialog == NULL)
+ editor->priv->hrule_dialog =
+ e_html_editor_hrule_dialog_new (editor);
+
+ gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
+}
+
+static void
+action_insert_table_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->table_dialog == NULL)
+ editor->priv->table_dialog =
+ e_html_editor_table_dialog_new (editor);
+
+ gtk_window_present (GTK_WINDOW (editor->priv->table_dialog));
+}
+
+static void
+action_insert_text_file_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Insert text file"), NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Text file"));
+ gtk_file_filter_add_mime_type (filter, "text/plain");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ GFile *file;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+ /* XXX Need a way to cancel this. */
+ g_file_load_contents_async (
+ file, NULL, (GAsyncReadyCallback)
+ insert_text_file_ready_cb,
+ g_object_ref (editor));
+
+ g_object_unref (file);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+action_language_cb (GtkToggleAction *toggle_action,
+ EHTMLEditor *editor)
+{
+ ESpellChecker *checker;
+ EHTMLEditorView *view;
+ const gchar *language_code;
+ GtkAction *add_action;
+ gchar *action_name;
+ gboolean active;
+
+ view = e_html_editor_get_view (editor);
+ checker = e_html_editor_view_get_spell_checker (view);
+ language_code = gtk_action_get_name (GTK_ACTION (toggle_action));
+
+ active = gtk_toggle_action_get_active (toggle_action);
+ e_spell_checker_set_language_active (checker, language_code, active);
+
+ /* Update "Add Word To" context menu item visibility. */
+ action_name = g_strdup_printf ("context-spell-add-%s", language_code);
+ add_action = e_html_editor_get_action (editor, action_name);
+ gtk_action_set_visible (add_action, active);
+ g_free (action_name);
+
+ editor_update_static_spell_actions (editor);
+
+ g_signal_emit_by_name (editor, "spell-languages-changed");
+}
+
+static gboolean
+update_mode_combobox (gpointer data)
+{
+ EHTMLEditor *editor = data;
+ EHTMLEditorView *view;
+ GtkAction *action;
+ gboolean is_html;
+
+ if (!E_IS_HTML_EDITOR (editor))
+ return FALSE;
+
+ view = e_html_editor_get_view (editor);
+ is_html = e_html_editor_view_get_html_mode (view);
+
+ action = gtk_action_group_get_action (
+ editor->priv->core_actions, "mode-html");
+ gtk_radio_action_set_current_value (
+ GTK_RADIO_ACTION (action), (is_html ? 1 : 0));
+
+ return FALSE;
+}
+
+static void
+action_mode_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EHTMLEditor *editor)
+{
+ GtkActionGroup *action_group;
+ EHTMLEditorView *view;
+ GtkWidget *style_combo_box;
+ gboolean is_html;
+
+ view = e_html_editor_get_view (editor);
+ is_html = e_html_editor_view_get_html_mode (view);
+
+ /* This must be done from idle callback, because apparently we can change
+ * current value in callback of current value change */
+ g_idle_add (update_mode_combobox, editor);
+
+ action_group = editor->priv->html_actions;
+ gtk_action_group_set_sensitive (action_group, is_html);
+
+ action_group = editor->priv->html_context_actions;
+ gtk_action_group_set_visible (action_group, is_html);
+
+ gtk_widget_set_sensitive (editor->priv->color_combo_box, is_html);
+
+ if (is_html) {
+ gtk_widget_show (editor->priv->html_toolbar);
+ } else {
+ gtk_widget_hide (editor->priv->html_toolbar);
+ }
+
+ /* Certain paragraph styles are HTML-only. */
+ gtk_action_set_sensitive (ACTION (STYLE_H1), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H1), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_H2), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H2), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_H3), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H3), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_H4), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H4), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_H5), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H5), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_H6), is_html);
+ gtk_action_set_visible (ACTION (STYLE_H6), is_html);
+ gtk_action_set_sensitive (ACTION (STYLE_ADDRESS), is_html);
+ gtk_action_set_visible (ACTION (STYLE_ADDRESS), is_html);
+
+ /* Hide them from the action combo box as well */
+ style_combo_box = e_html_editor_get_style_combo_box (editor);
+ e_action_combo_box_update_model (E_ACTION_COMBO_BOX (style_combo_box));
+}
+
+static void
+action_paste_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorView *view = e_html_editor_get_view (editor);
+
+ /* Paste only if WebView has focus */
+ if (gtk_widget_has_focus (GTK_WIDGET (view))) {
+ webkit_web_view_paste_clipboard (
+ WEBKIT_WEB_VIEW (view));
+
+ e_html_editor_view_force_spell_check (view);
+ }
+}
+
+static void
+action_paste_quote_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ e_html_editor_view_paste_clipboard_quoted (
+ e_html_editor_get_view (editor));
+
+ e_html_editor_view_force_spell_check (
+ e_html_editor_get_view (editor));
+}
+
+static void
+action_properties_cell_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->cell_dialog == NULL) {
+ editor->priv->cell_dialog =
+ e_html_editor_cell_dialog_new (editor);
+ }
+
+ e_html_editor_cell_dialog_show (
+ E_HTML_EDITOR_CELL_DIALOG (editor->priv->cell_dialog),
+ editor->priv->table_cell);
+}
+
+static void
+action_properties_image_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->image_dialog == NULL) {
+ editor->priv->image_dialog =
+ e_html_editor_image_dialog_new (editor);
+ }
+
+ e_html_editor_image_dialog_show (
+ E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog),
+ editor->priv->image);
+}
+
+static void
+action_properties_link_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->link_dialog == NULL) {
+ editor->priv->link_dialog =
+ e_html_editor_link_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
+}
+
+static void
+action_properties_page_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->page_dialog == NULL) {
+ editor->priv->page_dialog =
+ e_html_editor_page_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->page_dialog));
+}
+
+static void
+action_properties_paragraph_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->paragraph_dialog == NULL) {
+ editor->priv->paragraph_dialog =
+ e_html_editor_paragraph_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->paragraph_dialog));
+}
+
+static void
+action_properties_rule_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->hrule_dialog == NULL) {
+ editor->priv->hrule_dialog =
+ e_html_editor_hrule_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
+}
+
+static void
+action_properties_table_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->table_dialog == NULL) {
+ editor->priv->table_dialog =
+ e_html_editor_table_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->table_dialog));
+}
+
+static void
+action_properties_text_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->text_dialog == NULL) {
+ editor->priv->text_dialog =
+ e_html_editor_text_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->text_dialog));
+}
+
+static void
+action_redo_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ webkit_web_view_redo (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_select_all_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ webkit_web_view_select_all (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_show_find_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->find_dialog == NULL) {
+ editor->priv->find_dialog = e_html_editor_find_dialog_new (editor);
+ gtk_action_set_sensitive (ACTION (FIND_AGAIN), TRUE);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->find_dialog));
+}
+
+static void
+action_find_again_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->find_dialog == NULL) {
+ return;
+ }
+
+ e_html_editor_find_dialog_find_next (
+ E_HTML_EDITOR_FIND_DIALOG (editor->priv->find_dialog));
+}
+
+static void
+action_show_replace_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->replace_dialog == NULL) {
+ editor->priv->replace_dialog =
+ e_html_editor_replace_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->replace_dialog));
+}
+
+static void
+action_spell_check_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ if (editor->priv->spell_check_dialog == NULL) {
+ editor->priv->spell_check_dialog =
+ e_html_editor_spell_check_dialog_new (editor);
+ }
+
+ gtk_window_present (GTK_WINDOW (editor->priv->spell_check_dialog));
+}
+
+static void
+action_undo_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ webkit_web_view_undo (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_unindent_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ e_html_editor_selection_unindent (editor->priv->selection);
+}
+
+static void
+action_wrap_lines_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ e_html_editor_selection_wrap_lines (editor->priv->selection);
+}
+
+static void
+action_show_webkit_inspector_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitWebInspector *inspector;
+ EHTMLEditorView *view;
+
+ view = e_html_editor_get_view (editor);
+ inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
+
+ webkit_web_inspector_show (inspector);
+}
+
+/*****************************************************************************
+ * Core Actions
+ *
+ * These actions are always enabled.
+ *****************************************************************************/
+
+static GtkActionEntry core_entries[] = {
+
+ { "copy",
+ GTK_STOCK_COPY,
+ N_("_Copy"),
+ "<Control>c",
+ N_("Copy selected text to the clipboard"),
+ G_CALLBACK (action_copy_cb) },
+
+ { "cut",
+ GTK_STOCK_CUT,
+ N_("Cu_t"),
+ "<Control>x",
+ N_("Cut selected text to the clipboard"),
+ G_CALLBACK (action_cut_cb) },
+
+ { "indent",
+ GTK_STOCK_INDENT,
+ N_("_Increase Indent"),
+ "<Control>bracketright",
+ N_("Increase Indent"),
+ G_CALLBACK (action_indent_cb) },
+
+ { "insert-html-file",
+ NULL,
+ N_("_HTML File..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_insert_html_file_cb) },
+
+ { "insert-text-file",
+ NULL,
+ N_("Te_xt File..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_insert_text_file_cb) },
+
+ { "paste",
+ GTK_STOCK_PASTE,
+ N_("_Paste"),
+ "<Control>v",
+ N_("Paste text from the clipboard"),
+ G_CALLBACK (action_paste_cb) },
+
+ { "paste-quote",
+ NULL,
+ N_("Paste _Quotation"),
+ "<Shift><Control>v",
+ NULL,
+ G_CALLBACK (action_paste_quote_cb) },
+
+ { "redo",
+ GTK_STOCK_REDO,
+ N_("_Redo"),
+ "<Shift><Control>z",
+ N_("Redo the last undone action"),
+ G_CALLBACK (action_redo_cb) },
+
+ { "select-all",
+ GTK_STOCK_SELECT_ALL,
+ N_("Select _All"),
+ "<Control>a",
+ NULL,
+ G_CALLBACK (action_select_all_cb) },
+
+ { "show-find",
+ GTK_STOCK_FIND,
+ N_("_Find..."),
+ "<Control>f",
+ N_("Search for text"),
+ G_CALLBACK (action_show_find_cb) },
+
+ { "find-again",
+ NULL,
+ N_("Find A_gain"),
+ "<Control>g",
+ NULL,
+ G_CALLBACK (action_find_again_cb) },
+
+ { "show-replace",
+ GTK_STOCK_FIND_AND_REPLACE,
+ N_("Re_place..."),
+ "<Control>h",
+ N_("Search for and replace text"),
+ G_CALLBACK (action_show_replace_cb) },
+
+ { "spell-check",
+ GTK_STOCK_SPELL_CHECK,
+ N_("Check _Spelling..."),
+ "F7",
+ NULL,
+ G_CALLBACK (action_spell_check_cb) },
+
+ { "undo",
+ GTK_STOCK_UNDO,
+ N_("_Undo"),
+ "<Control>z",
+ N_("Undo the last action"),
+ G_CALLBACK (action_undo_cb) },
+
+ { "unindent",
+ GTK_STOCK_UNINDENT,
+ N_("_Decrease Indent"),
+ "<Control>bracketleft",
+ N_("Decrease Indent"),
+ G_CALLBACK (action_unindent_cb) },
+
+ { "wrap-lines",
+ NULL,
+ N_("_Wrap Lines"),
+ "<Control>k",
+ NULL,
+ G_CALLBACK (action_wrap_lines_cb) },
+
+ { "webkit-inspector",
+ NULL,
+ N_("Open Inspector"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_show_webkit_inspector_cb) },
+
+ /* Menus */
+
+ { "edit-menu",
+ NULL,
+ N_("_Edit"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "file-menu",
+ NULL,
+ N_("_File"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "format-menu",
+ NULL,
+ N_("For_mat"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "paragraph-style-menu",
+ NULL,
+ N_("_Paragraph Style"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "insert-menu",
+ NULL,
+ N_("_Insert"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "justify-menu",
+ NULL,
+ N_("_Alignment"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "language-menu",
+ NULL,
+ N_("Current _Languages"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "view-menu",
+ NULL,
+ N_("_View"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static GtkRadioActionEntry core_justify_entries[] = {
+
+ { "justify-center",
+ GTK_STOCK_JUSTIFY_CENTER,
+ N_("_Center"),
+ "<Control>e",
+ N_("Center Alignment"),
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER },
+
+ { "justify-left",
+ GTK_STOCK_JUSTIFY_LEFT,
+ N_("_Left"),
+ "<Control>l",
+ N_("Left Alignment"),
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT },
+
+ { "justify-right",
+ GTK_STOCK_JUSTIFY_RIGHT,
+ N_("_Right"),
+ "<Control>r",
+ N_("Right Alignment"),
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT }
+};
+
+static GtkRadioActionEntry core_mode_entries[] = {
+
+ { "mode-html",
+ NULL,
+ N_("_HTML"),
+ NULL,
+ N_("HTML editing mode"),
+ TRUE }, /* e_html_editor_view_set_html_mode */
+
+ { "mode-plain",
+ NULL,
+ N_("Plain _Text"),
+ NULL,
+ N_("Plain text editing mode"),
+ FALSE } /* e_html_editor_view_set_html_mode */
+};
+
+static GtkRadioActionEntry core_style_entries[] = {
+
+ { "style-normal",
+ NULL,
+ N_("_Normal"),
+ "<Control>0",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH },
+
+ { "style-h1",
+ NULL,
+ N_("Header _1"),
+ "<Control>1",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 },
+
+ { "style-h2",
+ NULL,
+ N_("Header _2"),
+ "<Control>2",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2 },
+
+ { "style-h3",
+ NULL,
+ N_("Header _3"),
+ "<Control>3",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3 },
+
+ { "style-h4",
+ NULL,
+ N_("Header _4"),
+ "<Control>4",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4 },
+
+ { "style-h5",
+ NULL,
+ N_("Header _5"),
+ "<Control>5",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5 },
+
+ { "style-h6",
+ NULL,
+ N_("Header _6"),
+ "<Control>6",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6 },
+
+ { "style-preformat",
+ NULL,
+ N_("_Preformatted"),
+ "<Control>7",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE },
+
+ { "style-address",
+ NULL,
+ N_("A_ddress"),
+ "<Control>8",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS },
+
+ { "style-blockquote",
+ NULL,
+ N_("_Blockquote"),
+ "<Control>9",
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE },
+
+ { "style-list-bullet",
+ NULL,
+ N_("_Bulleted List"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST },
+
+ { "style-list-roman",
+ NULL,
+ N_("_Roman Numeral List"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
+
+ { "style-list-number",
+ NULL,
+ N_("Numbered _List"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST },
+
+ { "style-list-alpha",
+ NULL,
+ N_("_Alphabetical List"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
+};
+
+/*****************************************************************************
+ * Core Actions (HTML only)
+ *
+ * These actions are only enabled in HTML mode.
+ *****************************************************************************/
+
+static GtkActionEntry html_entries[] = {
+
+ { "insert-image",
+ "insert-image",
+ N_("_Image..."),
+ NULL,
+ N_("Insert Image"),
+ G_CALLBACK (action_insert_image_cb) },
+
+ { "insert-link",
+ "insert-link",
+ N_("_Link..."),
+ NULL,
+ N_("Insert Link"),
+ G_CALLBACK (action_insert_link_cb) },
+
+ { "insert-rule",
+ "stock_insert-rule",
+ /* Translators: 'Rule' here means a horizontal line in an HTML text */
+ N_("_Rule..."),
+ NULL,
+ /* Translators: 'Rule' here means a horizontal line in an HTML text */
+ N_("Insert Rule"),
+ G_CALLBACK (action_insert_rule_cb) },
+
+ { "insert-table",
+ "stock_insert-table",
+ N_("_Table..."),
+ NULL,
+ N_("Insert Table"),
+ G_CALLBACK (action_insert_table_cb) },
+
+ { "properties-cell",
+ NULL,
+ N_("_Cell..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_cell_cb) },
+
+ { "properties-image",
+ NULL,
+ N_("_Image..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_image_cb) },
+
+ { "properties-link",
+ NULL,
+ N_("_Link..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_link_cb) },
+
+ { "properties-page",
+ NULL,
+ N_("Pa_ge..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_page_cb) },
+
+ { "properties-rule",
+ NULL,
+ /* Translators: 'Rule' here means a horizontal line in an HTML text */
+ N_("_Rule..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_rule_cb) },
+
+ { "properties-table",
+ NULL,
+ N_("_Table..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_table_cb) },
+
+ /* Menus */
+
+ { "font-size-menu",
+ NULL,
+ N_("Font _Size"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "font-style-menu",
+ NULL,
+ N_("_Font Style"),
+ NULL,
+ NULL,
+ NULL },
+};
+
+static GtkToggleActionEntry html_toggle_entries[] = {
+
+ { "bold",
+ GTK_STOCK_BOLD,
+ N_("_Bold"),
+ "<Control>b",
+ N_("Bold"),
+ NULL,
+ FALSE },
+
+ { "italic",
+ GTK_STOCK_ITALIC,
+ N_("_Italic"),
+ "<Control>i",
+ N_("Italic"),
+ NULL,
+ FALSE },
+
+ { "monospaced",
+ "stock_text-monospaced",
+ N_("_Plain Text"),
+ "<Control>t",
+ N_("Plain Text"),
+ NULL,
+ FALSE },
+
+ { "strikethrough",
+ GTK_STOCK_STRIKETHROUGH,
+ N_("_Strikethrough"),
+ NULL,
+ N_("Strikethrough"),
+ NULL,
+ FALSE },
+
+ { "underline",
+ GTK_STOCK_UNDERLINE,
+ N_("_Underline"),
+ "<Control>u",
+ N_("Underline"),
+ NULL,
+ FALSE }
+};
+
+static GtkRadioActionEntry html_size_entries[] = {
+
+ { "size-minus-two",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("-2"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY },
+
+ { "size-minus-one",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("-1"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL },
+
+ { "size-plus-zero",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("+0"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL },
+
+ { "size-plus-one",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("+1"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG },
+
+ { "size-plus-two",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("+2"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER },
+
+ { "size-plus-three",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("+3"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE },
+
+ { "size-plus-four",
+ NULL,
+ /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+ N_("+4"),
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE }
+};
+
+/*****************************************************************************
+ * Context Menu Actions
+ *
+ * These require separate action entries so we can toggle their visiblity
+ * rather than their sensitivity as we do with main menu / toolbar actions.
+ * Note that some of these actions use the same callback function as their
+ * non-context sensitive counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry context_entries[] = {
+
+ { "context-delete-cell",
+ NULL,
+ N_("Cell Contents"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_delete_cell_cb) },
+
+ { "context-delete-column",
+ NULL,
+ N_("Column"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_delete_column_cb) },
+
+ { "context-delete-row",
+ NULL,
+ N_("Row"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_delete_row_cb) },
+
+ { "context-delete-table",
+ NULL,
+ N_("Table"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_delete_table_cb) },
+
+ /* Menus */
+
+ { "context-delete-table-menu",
+ NULL,
+ /* Translators: Popup menu item caption, containing all the Delete options for a table */
+ N_("Table Delete"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "context-input-methods-menu",
+ NULL,
+ N_("Input Methods"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "context-insert-table-menu",
+ NULL,
+ /* Translators: Popup menu item caption, containing all the Insert options for a table */
+ N_("Table Insert"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "context-properties-menu",
+ NULL,
+ N_("Properties"),
+ NULL,
+ NULL,
+ NULL },
+};
+
+/*****************************************************************************
+ * Context Menu Actions (HTML only)
+ *
+ * These actions are never visible in plain-text mode. Note that some
+ * of them use the same callback function as their non-context sensitive
+ * counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry html_context_entries[] = {
+
+ { "context-insert-column-after",
+ NULL,
+ N_("Column After"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_insert_column_after_cb) },
+
+ { "context-insert-column-before",
+ NULL,
+ N_("Column Before"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_insert_column_before_cb) },
+
+ { "context-insert-link",
+ NULL,
+ N_("Insert _Link"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_insert_link_cb) },
+
+ { "context-insert-row-above",
+ NULL,
+ N_("Row Above"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_insert_row_above_cb) },
+
+ { "context-insert-row-below",
+ NULL,
+ N_("Row Below"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_insert_row_below_cb) },
+
+ { "context-insert-table",
+ NULL,
+ N_("Table"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_insert_table_cb) },
+
+ { "context-properties-cell",
+ NULL,
+ N_("Cell..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_cell_cb) },
+
+ { "context-properties-image",
+ NULL,
+ N_("Image..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_image_cb) },
+
+ { "context-properties-link",
+ NULL,
+ N_("Link..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_link_cb) },
+
+ { "context-properties-page",
+ NULL,
+ N_("Page..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_page_cb) },
+
+ { "context-properties-paragraph",
+ NULL,
+ N_("Paragraph..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_paragraph_cb) },
+
+ { "context-properties-rule",
+ NULL,
+ /* Translators: 'Rule' here means a horizontal line in an HTML text */
+ N_("Rule..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_rule_cb) },
+
+ { "context-properties-table",
+ NULL,
+ N_("Table..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_table_cb) },
+
+ { "context-properties-text",
+ NULL,
+ N_("Text..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_properties_text_cb) },
+
+ { "context-remove-link",
+ NULL,
+ N_("Remove Link"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_remove_link_cb) }
+};
+
+/*****************************************************************************
+ * Context Menu Actions (spell checking only)
+ *
+ * These actions are only visible when the word underneath the cursor is
+ * misspelled.
+ *****************************************************************************/
+
+static GtkActionEntry spell_context_entries[] = {
+
+ { "context-spell-add",
+ NULL,
+ N_("Add Word to Dictionary"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_spell_add_cb) },
+
+ { "context-spell-ignore",
+ NULL,
+ N_("Ignore Misspelled Word"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_context_spell_ignore_cb) },
+
+ { "context-spell-add-menu",
+ NULL,
+ N_("Add Word To"),
+ NULL,
+ NULL,
+ NULL },
+
+ /* Menus */
+
+ { "context-more-suggestions-menu",
+ NULL,
+ N_("More Suggestions"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static void
+editor_actions_setup_languages_menu (EHTMLEditor *editor)
+{
+ ESpellChecker *checker;
+ EHTMLEditorView *view;
+ GtkUIManager *manager;
+ GtkActionGroup *action_group;
+ GList *list, *link;
+ guint merge_id;
+
+ manager = editor->priv->manager;
+ action_group = editor->priv->language_actions;
+ view = e_html_editor_get_view (editor);
+ checker = e_html_editor_view_get_spell_checker (view);
+ merge_id = gtk_ui_manager_new_merge_id (manager);
+
+ list = e_spell_checker_list_available_dicts (checker);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary = link->data;
+ GtkToggleAction *action;
+ gboolean active;
+
+ action = gtk_toggle_action_new (
+ e_spell_dictionary_get_code (dictionary),
+ e_spell_dictionary_get_name (dictionary),
+ NULL, NULL);
+
+ /* Do this BEFORE connecting to the "toggled" signal.
+ * We're not prepared to invoke the signal handler yet.
+ * The "Add Word To" actions have not yet been added. */
+ active = e_spell_checker_get_language_active (
+ checker, e_spell_dictionary_get_code (dictionary));
+ gtk_toggle_action_set_active (action, active);
+
+ g_signal_connect (
+ action, "toggled",
+ G_CALLBACK (action_language_cb), editor);
+
+ gtk_action_group_add_action (
+ action_group, GTK_ACTION (action));
+
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (
+ manager, merge_id,
+ "/main-menu/edit-menu/language-menu",
+ e_spell_dictionary_get_code (dictionary),
+ e_spell_dictionary_get_code (dictionary),
+ GTK_UI_MANAGER_AUTO, FALSE);
+ }
+
+ g_list_free (list);
+}
+
+static void
+editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
+{
+ ESpellChecker *checker;
+ GtkUIManager *manager;
+ GtkActionGroup *action_group;
+ GList *available_dicts, *iter;
+ guint merge_id;
+
+ manager = editor->priv->manager;
+ action_group = editor->priv->spell_check_actions;;
+ checker = e_html_editor_view_get_spell_checker (editor->priv->html_editor_view);
+ available_dicts = e_spell_checker_list_available_dicts (checker);
+ merge_id = gtk_ui_manager_new_merge_id (manager);
+
+ for (iter = available_dicts; iter; iter = iter->next) {
+ ESpellDictionary *dictionary = iter->data;
+ GtkAction *action;
+ const gchar *code;
+ const gchar *name;
+ gchar *action_label;
+ gchar *action_name;
+
+ code = e_spell_dictionary_get_code (dictionary);
+ name = e_spell_dictionary_get_name (dictionary);
+
+ /* Add a suggestion menu. */
+ action_name = g_strdup_printf (
+ "context-spell-suggest-%s-menu", code);
+
+ action = gtk_action_new (action_name, name, NULL, NULL);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (
+ manager, merge_id,
+ "/context-menu/context-spell-suggest",
+ action_name, action_name,
+ GTK_UI_MANAGER_MENU, FALSE);
+
+ g_free (action_name);
+
+ /* Add an item to the "Add Word To" menu. */
+ action_name = g_strdup_printf ("context-spell-add-%s", code);
+ /* Translators: %s will be replaced with the actual dictionary
+ * name, where a user can add a word to. This is part of an
+ * "Add Word To" submenu. */
+ action_label = g_strdup_printf (_("%s Dictionary"), name);
+
+ action = gtk_action_new (
+ action_name, action_label, NULL, NULL);
+
+ g_signal_connect (
+ action, "activate",
+ G_CALLBACK (action_context_spell_add_cb), editor);
+
+ /* Visibility is dependent on whether the
+ * corresponding language action is active. */
+ gtk_action_set_visible (action, FALSE);
+
+ gtk_action_group_add_action (action_group, action);
+
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (
+ manager, merge_id,
+ "/context-menu/context-spell-add-menu",
+ action_name, action_name,
+ GTK_UI_MANAGER_AUTO, FALSE);
+
+ g_free (action_label);
+ g_free (action_name);
+ }
+
+ g_list_free (available_dicts);
+}
+
+void
+editor_actions_init (EHTMLEditor *editor)
+{
+ GtkAction *action;
+ GtkActionGroup *action_group;
+ GtkUIManager *manager;
+ const gchar *domain;
+ EHTMLEditorView *view;
+ GSettings *settings;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+ manager = e_html_editor_get_ui_manager (editor);
+ domain = GETTEXT_PACKAGE;
+ view = e_html_editor_get_view (editor);
+
+ /* Core Actions */
+ action_group = editor->priv->core_actions;
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, core_entries,
+ G_N_ELEMENTS (core_entries), editor);
+ gtk_action_group_add_radio_actions (
+ action_group, core_justify_entries,
+ G_N_ELEMENTS (core_justify_entries),
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+ NULL, NULL);
+ gtk_action_group_add_radio_actions (
+ action_group, core_mode_entries,
+ G_N_ELEMENTS (core_mode_entries),
+ TRUE,
+ G_CALLBACK (action_mode_cb), editor);
+ gtk_action_group_add_radio_actions (
+ action_group, core_style_entries,
+ G_N_ELEMENTS (core_style_entries),
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+ NULL, NULL);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ action = gtk_action_group_get_action (action_group, "mode-html");
+ g_object_bind_property (
+ view, "html-mode",
+ action, "current-value",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ /* Synchronize wiget mode with the buttons */
+ e_html_editor_view_set_html_mode (view, TRUE);
+
+ /* Face Action */
+ action = e_emoticon_action_new (
+ "insert-emoticon", _("_Emoticon"),
+ _("Insert Emoticon"), NULL);
+ g_object_set (action, "icon-name", "face-smile", NULL);
+ g_signal_connect (
+ action, "item-activated",
+ G_CALLBACK (action_insert_emoticon_cb), editor);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ /* Core Actions (HTML only) */
+ action_group = editor->priv->html_actions;
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, html_entries,
+ G_N_ELEMENTS (html_entries), editor);
+ gtk_action_group_add_toggle_actions (
+ action_group, html_toggle_entries,
+ G_N_ELEMENTS (html_toggle_entries), editor);
+ gtk_action_group_add_radio_actions (
+ action_group, html_size_entries,
+ G_N_ELEMENTS (html_size_entries),
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL,
+ NULL, NULL);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Context Menu Actions */
+ action_group = editor->priv->context_actions;
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, context_entries,
+ G_N_ELEMENTS (context_entries), editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Context Menu Actions (HTML only) */
+ action_group = editor->priv->html_context_actions;
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, html_context_entries,
+ G_N_ELEMENTS (html_context_entries), editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Context Menu Actions (spell check only) */
+ action_group = editor->priv->spell_check_actions;
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, spell_context_entries,
+ G_N_ELEMENTS (spell_context_entries), editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Language actions are generated dynamically. */
+ editor_actions_setup_languages_menu (editor);
+ action_group = editor->priv->language_actions;
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Some spell check actions are generated dynamically. */
+ action_group = editor->priv->suggestion_actions;
+ editor_actions_setup_spell_check_menu (editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ /* Do this after all language actions are initialized. */
+ editor_update_static_spell_actions (editor);
+
+ /* Fine Tuning */
+
+ g_object_set (
+ G_OBJECT (ACTION (SHOW_FIND)),
+ "short-label", _("_Find"), NULL);
+ g_object_set (
+ G_OBJECT (ACTION (SHOW_REPLACE)),
+ "short-label", _("Re_place"), NULL);
+ g_object_set (
+ G_OBJECT (ACTION (INSERT_IMAGE)),
+ "short-label", _("_Image"), NULL);
+ g_object_set (
+ G_OBJECT (ACTION (INSERT_LINK)),
+ "short-label", _("_Link"), NULL);
+ g_object_set (
+ G_OBJECT (ACTION (INSERT_RULE)),
+ /* Translators: 'Rule' here means a horizontal line in an HTML text */
+ "short-label", _("_Rule"), NULL);
+ g_object_set (
+ G_OBJECT (ACTION (INSERT_TABLE)),
+ "short-label", _("_Table"), NULL);
+
+ gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
+ gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
+ gtk_action_set_sensitive (ACTION (SPELL_CHECK), FALSE);
+
+ g_object_bind_property (
+ view, "can-redo",
+ ACTION (REDO), "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "can-undo",
+ ACTION (UNDO), "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "can-copy",
+ ACTION (COPY), "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "can-cut",
+ ACTION (CUT), "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "can-paste",
+ ACTION (PASTE), "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /* This is connected to JUSTIFY_LEFT action only, but
+ * it automatically applies on all actions in the group. */
+ g_object_bind_property (
+ editor->priv->selection, "alignment",
+ ACTION (JUSTIFY_LEFT), "current-value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "bold",
+ ACTION (BOLD), "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "font-size",
+ ACTION (FONT_SIZE_GROUP), "current-value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "block-format",
+ ACTION (STYLE_NORMAL), "current-value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "indented",
+ ACTION (UNINDENT), "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ editor->priv->selection, "italic",
+ ACTION (ITALIC), "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "monospaced",
+ ACTION (MONOSPACED), "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "strikethrough",
+ ACTION (STRIKETHROUGH), "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ editor->priv->selection, "underline",
+ ACTION (UNDERLINE), "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ /* Disable all actions and toolbars when editor is not editable */
+ g_object_bind_property (
+ view, "editable",
+ editor->priv->core_actions, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "editable",
+ editor->priv->html_actions, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "editable",
+ editor->priv->spell_check_actions, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ view, "editable",
+ editor->priv->suggestion_actions, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ gtk_action_set_visible (
+ ACTION (WEBKIT_INSPECTOR),
+ g_settings_get_boolean (settings, "composer-developer-mode"));
+ g_object_unref (settings);
+}
diff --git a/e-util/e-html-editor-actions.h b/e-util/e-html-editor-actions.h
new file mode 100644
index 0000000000..8999add9dc
--- /dev/null
+++ b/e-util/e-html-editor-actions.h
@@ -0,0 +1,155 @@
+/* e-html-editor-actions.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_ACTIONS_H
+#define E_HTML_EDITOR_ACTIONS_H
+
+#define E_HTML_EDITOR_ACTION(editor, name) \
+ (e_html_editor_get_action (E_HTML_EDITOR (editor), (name)))
+
+#define E_HTML_EDITOR_ACTION_BOLD(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "bold")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_CELL(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-delete-cell")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_COLUMN(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-delete-column")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_ROW(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-delete-row")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_TABLE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-delete-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_AFTER(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-insert-column-after")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_BEFORE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-insert-column-before")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_ABOVE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-insert-row-above")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-insert-row-below")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_TABLE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-insert-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-cell")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-image")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_LINK(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-link")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_PARAGRAPH(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-paragraph")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_RULE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-rule")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_TABLE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_TEXT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-properties-text")
+#define E_HTML_EDITOR_ACTION_CONTEXT_REMOVE_LINK(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-remove-link")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-spell-add")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD_MENU(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-spell-add-menu")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_IGNORE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "context-spell-ignore")
+#define E_HTML_EDITOR_ACTION_COPY(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "copy")
+#define E_HTML_EDITOR_ACTION_CUT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "cut")
+#define E_HTML_EDITOR_ACTION_EDIT_MENU(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "edit-menu")
+#define E_HTML_EDITOR_ACTION_FIND_AGAIN(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "find-again")
+#define E_HTML_EDITOR_ACTION_FONT_SIZE_GROUP(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "size-plus-zero")
+#define E_HTML_EDITOR_ACTION_FORMAT_MENU(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "format-menu")
+#define E_HTML_EDITOR_ACTION_FORMAT_TEXT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "format-text")
+#define E_HTML_EDITOR_ACTION_INSERT_IMAGE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "insert-image")
+#define E_HTML_EDITOR_ACTION_INSERT_LINK(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "insert-link")
+#define E_HTML_EDITOR_ACTION_INSERT_MENU(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "insert-menu")
+#define E_HTML_EDITOR_ACTION_INSERT_RULE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "insert-rule")
+#define E_HTML_EDITOR_ACTION_INSERT_TABLE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "insert-table")
+#define E_HTML_EDITOR_ACTION_ITALIC(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "italic")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_CENTER(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "justify-center")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_LEFT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "justify-left")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_RIGHT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "justify-right")
+#define E_HTML_EDITOR_ACTION_MODE_HTML(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "mode-html")
+#define E_HTML_EDITOR_ACTION_MODE_PLAIN(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "mode-plain")
+#define E_HTML_EDITOR_ACTION_MONOSPACED(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "monospaced")
+#define E_HTML_EDITOR_ACTION_PASTE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "paste")
+#define E_HTML_EDITOR_ACTION_PROPERTIES_RULE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "properties-rule")
+#define E_HTML_EDITOR_ACTION_PROPERTIES_TABLE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "properties-table")
+#define E_HTML_EDITOR_ACTION_REDO(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "redo")
+#define E_HTML_EDITOR_ACTION_SHOW_FIND(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "show-find")
+#define E_HTML_EDITOR_ACTION_SHOW_REPLACE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "show-replace")
+#define E_HTML_EDITOR_ACTION_SIZE_PLUS_ZERO(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "size-plus-zero")
+#define E_HTML_EDITOR_ACTION_SPELL_CHECK(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "spell-check")
+#define E_HTML_EDITOR_ACTION_STRIKETHROUGH(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "strikethrough")
+#define E_HTML_EDITOR_ACTION_STYLE_ADDRESS(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-address")
+#define E_HTML_EDITOR_ACTION_STYLE_H1(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h1")
+#define E_HTML_EDITOR_ACTION_STYLE_H2(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h2")
+#define E_HTML_EDITOR_ACTION_STYLE_H3(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h3")
+#define E_HTML_EDITOR_ACTION_STYLE_H4(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h4")
+#define E_HTML_EDITOR_ACTION_STYLE_H5(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h5")
+#define E_HTML_EDITOR_ACTION_STYLE_H6(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-h6")
+#define E_HTML_EDITOR_ACTION_STYLE_NORMAL(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "style-normal")
+#define E_HTML_EDITOR_ACTION_TEST_URL(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "test-url")
+#define E_HTML_EDITOR_ACTION_UNDERLINE(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "underline")
+#define E_HTML_EDITOR_ACTION_UNDO(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "undo")
+#define E_HTML_EDITOR_ACTION_UNINDENT(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "unindent")
+#define E_HTML_EDITOR_ACTION_WEBKIT_INSPECTOR(editor) \
+ E_HTML_EDITOR_ACTION ((editor), "webkit-inspector")
+
+#endif /* E_HTML_EDITOR_ACTIONS_H */
diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c
new file mode 100644
index 0000000000..2b487288ae
--- /dev/null
+++ b/e-util/e-html-editor-cell-dialog.c
@@ -0,0 +1,872 @@
+/*
+ * e-html-editor-cell-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-cell-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+
+#include "e-color-combo.h"
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogPrivate))
+
+struct _EHTMLEditorCellDialogPrivate {
+ GtkWidget *scope_cell_button;
+ GtkWidget *scope_table_button;
+ GtkWidget *scope_row_button;
+ GtkWidget *scope_column_button;
+
+ GtkWidget *halign_combo;
+ GtkWidget *valign_combo;
+
+ GtkWidget *wrap_text_check;
+ GtkWidget *header_style_check;
+
+ GtkWidget *width_check;
+ GtkWidget *width_edit;
+ GtkWidget *width_units;
+
+ GtkWidget *row_span_edit;
+ GtkWidget *col_span_edit;
+
+ GtkWidget *background_color_picker;
+ GtkWidget *background_image_chooser;
+
+ WebKitDOMElement *cell;
+ guint scope;
+};
+
+enum {
+ SCOPE_CELL,
+ SCOPE_ROW,
+ SCOPE_COLUMN,
+ SCOPE_TABLE
+} DialogScope;
+
+static GdkRGBA white = { 1, 1, 1, 1 };
+
+typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data);
+typedef void (*DOMUlongFunc) (WebKitDOMHTMLTableCellElement *cell, gulong val, gpointer user_data);
+typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data);
+
+G_DEFINE_TYPE (
+ EHTMLEditorCellDialog,
+ e_html_editor_cell_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell,
+ gpointer func,
+ GValue *value,
+ gpointer user_data)
+{
+ if (G_VALUE_HOLDS_STRING (value)) {
+ DOMStrFunc f = func;
+ f (cell, g_value_get_string (value), user_data);
+ } else if (G_VALUE_HOLDS_ULONG (value)) {
+ DOMUlongFunc f = func;
+ f (cell, g_value_get_ulong (value), user_data);
+ } else if (G_VALUE_HOLDS_BOOLEAN (value)) {
+ DOMBoolFunc f = func;
+ f (cell, g_value_get_boolean (value), user_data);
+ }
+}
+
+static void
+for_each_cell_do (WebKitDOMElement *row,
+ gpointer func,
+ GValue *value,
+ gpointer user_data)
+{
+ WebKitDOMHTMLCollection *cells;
+ gulong ii, length;
+ cells = webkit_dom_html_table_row_element_get_cells (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+ length = webkit_dom_html_collection_get_length (cells);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *cell;
+ cell = webkit_dom_html_collection_item (cells, ii);
+ if (!cell) {
+ continue;
+ }
+
+ call_cell_dom_func (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data);
+ }
+}
+
+static void
+html_editor_cell_dialog_set_attribute (EHTMLEditorCellDialog *dialog,
+ gpointer func,
+ GValue *value,
+ gpointer user_data)
+{
+ if (dialog->priv->scope == SCOPE_CELL) {
+
+ call_cell_dom_func (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell),
+ func, value, user_data);
+
+ } else if (dialog->priv->scope == SCOPE_COLUMN) {
+ gulong index, ii, length;
+ WebKitDOMElement *table;
+ WebKitDOMHTMLCollection *rows;
+
+ index = webkit_dom_html_table_cell_element_get_cell_index (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+ table = e_html_editor_dom_node_find_parent_element (
+ WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+ if (!table) {
+ return;
+ }
+
+ rows = webkit_dom_html_table_element_get_rows (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+ length = webkit_dom_html_collection_get_length (rows);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *row, *cell;
+ WebKitDOMHTMLCollection *cells;
+
+ row = webkit_dom_html_collection_item (rows, ii);
+ cells = webkit_dom_html_table_row_element_get_cells (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+ cell = webkit_dom_html_collection_item (cells, index);
+ if (!cell) {
+ continue;
+ }
+
+ call_cell_dom_func (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell),
+ func, value, user_data);
+ }
+
+ } else if (dialog->priv->scope == SCOPE_ROW) {
+ WebKitDOMElement *row;
+
+ row = e_html_editor_dom_node_find_parent_element (
+ WEBKIT_DOM_NODE (dialog->priv->cell), "TR");
+ if (!row) {
+ return;
+ }
+
+ for_each_cell_do (row, func, value, user_data);
+
+ } else if (dialog->priv->scope == SCOPE_TABLE) {
+ gulong ii, length;
+ WebKitDOMElement *table;
+ WebKitDOMHTMLCollection *rows;
+
+ table = e_html_editor_dom_node_find_parent_element (
+ WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+ if (!table) {
+ return;
+ }
+
+ rows = webkit_dom_html_table_element_get_rows (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+ length = webkit_dom_html_collection_get_length (rows);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *row;
+
+ row = webkit_dom_html_collection_item (rows, ii);
+ if (!row) {
+ continue;
+ }
+
+ for_each_cell_do (
+ WEBKIT_DOM_ELEMENT (row), func, value, user_data);
+ }
+ }
+}
+
+static void
+html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog)
+{
+ if (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button))) {
+
+ dialog->priv->scope = SCOPE_CELL;
+
+ } else if (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->scope_row_button))) {
+
+ dialog->priv->scope = SCOPE_ROW;
+
+ } else if (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->scope_column_button))) {
+
+ dialog->priv->scope = SCOPE_COLUMN;
+
+ } else if (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->scope_table_button))) {
+
+ dialog->priv->scope = SCOPE_TABLE;
+
+ }
+}
+
+static void
+html_editor_cell_dialog_set_valign (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (
+ &val,
+ gtk_combo_box_get_active_id (
+ GTK_COMBO_BOX (dialog->priv->valign_combo)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_v_align, &val, NULL);
+
+ g_value_unset (&val);
+}
+
+static void
+html_editor_cell_dialog_set_halign (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (
+ &val,
+ gtk_combo_box_get_active_id (
+ GTK_COMBO_BOX (dialog->priv->halign_combo)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_align, &val, NULL);
+
+ g_value_unset (&val);
+}
+
+static void
+html_editor_cell_dialog_set_wrap_text (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (
+ &val,
+ !gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL);
+}
+
+static void
+cell_set_header_style (WebKitDOMHTMLTableCellElement *cell,
+ gboolean header_style,
+ gpointer user_data)
+{
+ EHTMLEditorCellDialog *dialog = user_data;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *nodes;
+ WebKitDOMElement *new_cell;
+ gulong length, ii;
+ gchar *tagname;
+
+ document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell));
+ tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell));
+
+ if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) {
+
+ new_cell = webkit_dom_document_create_element (document, "TH", NULL);
+
+ } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) {
+
+ new_cell = webkit_dom_document_create_element (document, "TD", NULL);
+
+ } else {
+ g_free (tagname);
+ return;
+ }
+
+ /* Move all child nodes from cell to new_cell */
+ nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell));
+ length = webkit_dom_node_list_get_length (nodes);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_node_list_item (nodes, ii);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (new_cell), node, NULL);
+ }
+
+ /* Insert new_cell before cell and remove cell */
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (cell)),
+ WEBKIT_DOM_NODE (new_cell),
+ WEBKIT_DOM_NODE (cell), NULL);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
+ WEBKIT_DOM_NODE (cell), NULL);
+
+ dialog->priv->cell = new_cell;
+
+ g_free (tagname);
+}
+
+static void
+html_editor_cell_dialog_set_header_style (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (
+ &val,
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->header_style_check)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, cell_set_header_style, &val, dialog);
+}
+
+static void
+html_editor_cell_dialog_set_width (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+ gchar *width;
+
+ if (!gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
+
+ width = g_strdup ("auto");
+ } else {
+
+ width = g_strdup_printf (
+ "%d%s",
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+ ((gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ?
+ "px" : "%"));
+ }
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_take_string (&val, width);
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_width, &val, NULL);
+
+ g_free (width);
+}
+
+static void
+html_editor_cell_dialog_set_column_span (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_ULONG);
+ g_value_set_ulong (
+ &val,
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->col_span_edit)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_col_span, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_set_row_span (EHTMLEditorCellDialog *dialog)
+{
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_ULONG);
+ g_value_set_ulong (
+ &val,
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->row_span_edit)));
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_row_span, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_set_background_color (EHTMLEditorCellDialog *dialog)
+{
+ gchar *color;
+ GdkRGBA rgba;
+ GValue val = { 0 };
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+ color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_take_string (&val, color);
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL);
+
+ g_free (color);
+}
+
+static void
+cell_set_background_image (WebKitDOMHTMLTableCellElement *cell,
+ const gchar *uri,
+ gpointer user_data)
+{
+ if (!uri || !*uri) {
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (cell), "background");
+ } else {
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (cell), "background", uri, NULL);
+ }
+}
+
+static void
+html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog)
+{
+ const gchar *uri;
+ GValue val = { 0 };
+
+ uri = gtk_file_chooser_get_uri (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_take_string (&val, (gchar *) uri);
+
+ html_editor_cell_dialog_set_attribute (
+ dialog, cell_set_background_image, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorCellDialog *dialog;
+ gchar *tmp;
+ GdkRGBA color;
+
+ dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
+
+ tmp = webkit_dom_html_table_cell_element_get_align (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->halign_combo),
+ (tmp && *tmp) ? tmp : "left");
+ g_free (tmp);
+
+ tmp = webkit_dom_html_table_cell_element_get_v_align (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->valign_combo),
+ (tmp && *tmp) ? tmp : "middle");
+ g_free (tmp);
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check),
+ !webkit_dom_html_table_cell_element_get_no_wrap (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+
+ tmp = webkit_dom_element_get_tag_name (
+ WEBKIT_DOM_ELEMENT (dialog->priv->cell));
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->header_style_check),
+ (g_ascii_strncasecmp (tmp, "TH", 2) == 0));
+ g_free (tmp);
+
+ tmp = webkit_dom_html_table_cell_element_get_width (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+ if (tmp && *tmp) {
+ gint val = atoi (tmp);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+ } else {
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), 0);
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
+ }
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
+ g_free (tmp);
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->row_span_edit),
+ webkit_dom_html_table_cell_element_get_row_span (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->col_span_edit),
+ webkit_dom_html_table_cell_element_get_col_span (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+
+ if (webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background")) {
+ tmp = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background");
+
+ gtk_file_chooser_set_uri (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_chooser),
+ tmp);
+
+ g_free (tmp);
+ } else {
+ gtk_file_chooser_unselect_all (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
+ }
+
+ tmp = webkit_dom_html_table_cell_element_get_bg_color (
+ WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+ if (!tmp || *tmp) {
+ color = white;
+ }
+ if (gdk_rgba_parse (&color, tmp)) {
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker),
+ &color);
+ } else {
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker),
+ &white);
+ }
+ g_free (tmp);
+
+ GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_cell_dialog_class_init (EHTMLEditorCellDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorCellDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_cell_dialog_show;
+}
+
+static void
+e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog)
+{
+ GtkGrid *main_layout, *grid;
+ GtkWidget *widget;
+ GtkFileFilter *file_filter;
+
+ dialog->priv = E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == Scope == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Scope</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Scope: cell */
+ widget = gtk_image_new_from_icon_name ("stock_select-cell", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ widget = gtk_radio_button_new_with_mnemonic (NULL, _("C_ell"));
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->scope_cell_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+ /* Scope: row */
+ widget = gtk_image_new_from_icon_name ("stock_select-row", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+ widget = gtk_radio_button_new_with_mnemonic_from_widget (
+ GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("_Row"));
+ gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+ dialog->priv->scope_row_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+ /* Scope: table */
+ widget = gtk_image_new_from_icon_name ("stock_select-table", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ widget = gtk_radio_button_new_with_mnemonic_from_widget (
+ GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("_Table"));
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ dialog->priv->scope_table_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+ /* Scope: column */
+ widget = gtk_image_new_from_icon_name ("stock_select-column", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+
+ widget = gtk_radio_button_new_with_mnemonic_from_widget (
+ GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("Col_umn"));
+ gtk_grid_attach (grid, widget, 3, 1, 1, 1);
+ dialog->priv->scope_column_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+ /* == Alignment & Behavior == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Alignment &amp; Behavior</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Horizontal */
+ widget = gtk_combo_box_text_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->halign_combo = widget;
+
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_cell_dialog_set_halign), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("_Horizontal:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->halign_combo);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Vertical */
+ widget = gtk_combo_box_text_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "top", _("Top"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "middle", _("Middle"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "bottom", _("Bottom"));
+ gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+ dialog->priv->valign_combo = widget;
+
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_cell_dialog_set_valign), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("_Vertical:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->valign_combo);
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+ /* Wrap Text */
+ widget = gtk_check_button_new_with_mnemonic (_("_Wrap Text"));
+ dialog->priv->wrap_text_check = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_wrap_text), dialog);
+
+ /* Header Style */
+ widget = gtk_check_button_new_with_mnemonic (_("_Header Style"));
+ dialog->priv->header_style_check = widget;
+
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_cell_dialog_set_header_style), dialog);
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_pack_start (GTK_BOX (widget), dialog->priv->wrap_text_check, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (widget), dialog->priv->header_style_check, FALSE, FALSE, 0);
+ gtk_grid_attach (grid, widget, 0, 1, 4, 1);
+
+ /* == Layout == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Width */
+ widget = gtk_check_button_new_with_mnemonic (_("_Width"));
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+ dialog->priv->width_check = widget;
+
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->width_edit = widget;
+
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_cell_dialog_set_width), dialog);
+ g_object_bind_property (
+ dialog->priv->width_check, "active",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-px", "px");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-percent", "%");
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ dialog->priv->width_units = widget;
+
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_cell_dialog_set_width), dialog);
+ g_object_bind_property (
+ dialog->priv->width_check, "active",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /* Row Span */
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+ dialog->priv->row_span_edit = widget;
+
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_cell_dialog_set_row_span), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("Row S_pan:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->row_span_edit);
+ gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+
+ /* Column Span */
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+ dialog->priv->col_span_edit = widget;
+
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_cell_dialog_set_column_span), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("Co_lumn Span:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->col_span_edit);
+ gtk_grid_attach (grid, widget, 3, 1, 1, 1);
+
+ /* == Background == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 6, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 7, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Color */
+ widget = e_color_combo_new ();
+ e_color_combo_set_default_color (E_COLOR_COMBO (widget), &white);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_cell_dialog_set_background_color), dialog);
+ dialog->priv->background_color_picker = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("C_olor:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_color_picker);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Image */
+ widget = e_image_chooser_dialog_new (
+ _("Choose Background Image"),
+ GTK_WINDOW (dialog));
+ dialog->priv->background_image_chooser = widget;
+
+ file_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (file_filter, _("Images"));
+ gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+ widget = gtk_file_chooser_button_new_with_dialog (
+ dialog->priv->background_image_chooser);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), file_filter);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "file-set",
+ G_CALLBACK (html_editor_cell_dialog_set_background_image), dialog);
+ dialog->priv->background_image_chooser = widget;
+
+ widget =gtk_label_new_with_mnemonic (_("_Image:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_image_chooser);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_cell_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_CELL_DIALOG,
+ "editor", editor,
+ "title", N_("Cell Properties"),
+ NULL));
+}
+
+void
+e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog,
+ WebKitDOMNode *cell)
+{
+ EHTMLEditorCellDialogClass *class;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_CELL_DIALOG (dialog));
+ g_return_if_fail (cell != NULL);
+
+ dialog->priv->cell = e_html_editor_dom_node_find_parent_element (cell, "TD");
+ if (dialog->priv->cell == NULL) {
+ dialog->priv->cell =
+ e_html_editor_dom_node_find_parent_element (cell, "TH");
+ }
+
+ class = E_HTML_EDITOR_CELL_DIALOG_GET_CLASS (dialog);
+ GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
+}
+
diff --git a/e-util/e-html-editor-cell-dialog.h b/e-util/e-html-editor-cell-dialog.h
new file mode 100644
index 0000000000..3a7f60822f
--- /dev/null
+++ b/e-util/e-html-editor-cell-dialog.h
@@ -0,0 +1,72 @@
+/*
+ * e-html-editor-cell-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_CELL_DIALOG_H
+#define E_HTML_EDITOR_CELL_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_CELL_DIALOG \
+ (e_html_editor_cell_dialog_get_type ())
+#define E_HTML_EDITOR_CELL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialog))
+#define E_HTML_EDITOR_CELL_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogClass))
+#define E_IS_HTML_EDITOR_CELL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG))
+#define E_IS_HTML_EDITOR_CELL_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_CELL_DIALOG))
+#define E_HTML_EDITOR_CELL_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorCellDialog EHTMLEditorCellDialog;
+typedef struct _EHTMLEditorCellDialogClass EHTMLEditorCellDialogClass;
+typedef struct _EHTMLEditorCellDialogPrivate EHTMLEditorCellDialogPrivate;
+
+struct _EHTMLEditorCellDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorCellDialogPrivate *priv;
+};
+
+struct _EHTMLEditorCellDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_cell_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_cell_dialog_new (EHTMLEditor *editor);
+void e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog,
+ WebKitDOMNode *cell);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_CELL_DIALOG_H */
diff --git a/e-util/e-html-editor-dialog.c b/e-util/e-html-editor-dialog.c
new file mode 100644
index 0000000000..5b43ebd316
--- /dev/null
+++ b/e-util/e-html-editor-dialog.c
@@ -0,0 +1,248 @@
+/*
+ * e-html-editor-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-dialog.h"
+
+#define E_HTML_EDITOR_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogPrivate))
+
+struct _EHTMLEditorDialogPrivate {
+ EHTMLEditor *editor;
+
+ GtkBox *button_box;
+ GtkGrid *container;
+};
+
+enum {
+ PROP_0,
+ PROP_EDITOR,
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+ EHTMLEditorDialog,
+ e_html_editor_dialog,
+ GTK_TYPE_WINDOW);
+
+static void
+html_editor_dialog_set_editor (EHTMLEditorDialog *dialog,
+ EHTMLEditor *editor)
+{
+ dialog->priv->editor = g_object_ref (editor);
+
+ g_object_notify (G_OBJECT (dialog), "editor");
+}
+
+static void
+html_editor_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITOR:
+ g_value_set_object (
+ value,
+ e_html_editor_dialog_get_editor (
+ E_HTML_EDITOR_DIALOG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITOR:
+ html_editor_dialog_set_editor (
+ E_HTML_EDITOR_DIALOG (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_dialog_constructed (GObject *object)
+{
+ EHTMLEditorDialog *dialog = E_HTML_EDITOR_DIALOG (object);
+
+ /* Chain up to parent implementation first */
+ G_OBJECT_CLASS (e_html_editor_dialog_parent_class)->constructed (object);
+
+ gtk_window_set_transient_for (
+ GTK_WINDOW (dialog),
+ GTK_WINDOW (gtk_widget_get_toplevel (
+ GTK_WIDGET (dialog->priv->editor))));
+}
+
+static void
+html_editor_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (widget);
+
+ gtk_widget_show_all (GTK_WIDGET (priv->container));
+ gtk_widget_show_all (GTK_WIDGET (priv->button_box));
+
+ GTK_WIDGET_CLASS (e_html_editor_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_dialog_dispose (GObject *object)
+{
+ EHTMLEditorDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (object);
+
+ g_clear_object (&priv->editor);
+
+ /* Chain up to parent's implementation */
+ G_OBJECT_CLASS (e_html_editor_dialog_parent_class)->dispose (object);
+}
+
+static void
+e_html_editor_dialog_class_init (EHTMLEditorDialogClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = html_editor_dialog_get_property;
+ object_class->set_property = html_editor_dialog_set_property;
+ object_class->dispose = html_editor_dialog_dispose;
+ object_class->constructed = html_editor_dialog_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_dialog_show;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EDITOR,
+ g_param_spec_object (
+ "editor",
+ NULL,
+ NULL,
+ E_TYPE_HTML_EDITOR,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+}
+
+static gboolean
+key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ if (event->keyval == GDK_KEY_Escape) {
+ gtk_widget_hide (widget);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+e_html_editor_dialog_init (EHTMLEditorDialog *dialog)
+{
+ GtkBox *main_layout;
+ GtkGrid *grid;
+ GtkWidget *widget, *button_box;
+
+ dialog->priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 5));
+ gtk_container_add (GTK_CONTAINER (dialog), GTK_WIDGET (main_layout));
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 10);
+ gtk_grid_set_column_spacing (grid, 10);
+ gtk_box_pack_start (main_layout, GTK_WIDGET (grid), TRUE, TRUE, 5);
+ dialog->priv->container = grid;
+
+ /* == Button box == */
+ widget = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (gtk_widget_hide), dialog);
+
+ button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing (GTK_BOX (button_box), 5);
+ gtk_box_pack_start (main_layout, button_box, TRUE, TRUE, 5);
+ gtk_box_pack_start (GTK_BOX (button_box), widget, FALSE, FALSE, 5);
+ dialog->priv->button_box = GTK_BOX (button_box);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+
+ g_object_set (
+ G_OBJECT (dialog),
+ "destroy-with-parent", TRUE,
+ "modal", TRUE,
+ "resizable", FALSE,
+ "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+ NULL);
+
+ /* Don't destroy the dialog when closed! */
+ g_signal_connect (
+ dialog, "delete-event",
+ G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+
+ g_signal_connect (
+ dialog, "key-press-event",
+ G_CALLBACK (key_press_event_cb), NULL);
+}
+
+EHTMLEditor *
+e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+ return dialog->priv->editor;
+}
+
+GtkGrid *
+e_html_editor_dialog_get_container (EHTMLEditorDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+ return dialog->priv->container;
+}
+
+GtkBox *
+e_html_editor_dialog_get_button_box (EHTMLEditorDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+ return dialog->priv->button_box;
+}
+
diff --git a/e-util/e-html-editor-dialog.h b/e-util/e-html-editor-dialog.h
new file mode 100644
index 0000000000..37fc7a53c3
--- /dev/null
+++ b/e-util/e-html-editor-dialog.h
@@ -0,0 +1,74 @@
+/*
+ * e-html-editor-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_DIALOG_H
+#define E_HTML_EDITOR_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-html-editor.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_DIALOG \
+ (e_html_editor_dialog_get_type ())
+#define E_HTML_EDITOR_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialog))
+#define E_HTML_EDITOR_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogClass))
+#define E_IS_HTML_EDITOR_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_DIALOG))
+#define E_IS_HTML_EDITOR_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_DIALOG))
+#define E_HTML_EDITOR_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorDialog EHTMLEditorDialog;
+typedef struct _EHTMLEditorDialogClass EHTMLEditorDialogClass;
+typedef struct _EHTMLEditorDialogPrivate EHTMLEditorDialogPrivate;
+
+struct _EHTMLEditorDialog {
+ GtkWindow parent;
+ EHTMLEditorDialogPrivate *priv;
+};
+
+struct _EHTMLEditorDialogClass {
+ GtkWindowClass parent_class;
+};
+
+GType e_html_editor_dialog_get_type (void) G_GNUC_CONST;
+EHTMLEditor * e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog);
+GtkBox * e_html_editor_dialog_get_button_box
+ (EHTMLEditorDialog *dialog);
+GtkGrid * e_html_editor_dialog_get_container
+ (EHTMLEditorDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_DIALOG_H */
diff --git a/e-util/e-html-editor-find-dialog.c b/e-util/e-html-editor-find-dialog.c
new file mode 100644
index 0000000000..9f44cf8dc5
--- /dev/null
+++ b/e-util/e-html-editor-find-dialog.c
@@ -0,0 +1,224 @@
+/*
+ * e-html-editor-find-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-find-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#define E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogPrivate))
+
+struct _EHTMLEditorFindDialogPrivate {
+ GtkWidget *entry;
+ GtkWidget *backwards;
+ GtkWidget *case_sensitive;
+ GtkWidget *wrap_search;
+
+ GtkWidget *find_button;
+
+ GtkWidget *result_label;
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorFindDialog,
+ e_html_editor_find_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+reset_dialog (EHTMLEditorFindDialog *dialog)
+{
+ gtk_widget_set_sensitive (dialog->priv->find_button, TRUE);
+ gtk_widget_hide (dialog->priv->result_label);
+}
+
+static void
+html_editor_find_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
+
+ reset_dialog (dialog);
+ gtk_widget_grab_focus (dialog->priv->entry);
+
+ /* Chain up to parent's implementation */
+ GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog)
+{
+ gboolean found;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ found = webkit_web_view_search_text (
+ WEBKIT_WEB_VIEW (view),
+ gtk_entry_get_text (
+ GTK_ENTRY (dialog->priv->entry)),
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (
+ dialog->priv->case_sensitive)),
+ !gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (
+ dialog->priv->backwards)),
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (
+ dialog->priv->wrap_search)));
+
+ gtk_widget_set_sensitive (dialog->priv->find_button, found);
+
+ /* We give focus to WebKit so that the selection is highlited.
+ * Without focus selection is not visible (at least with my default
+ * color scheme). The focus in fact is not given to WebKit, because
+ * this dialog is modal, but it satisfies it in a way that it paints
+ * the selection :) */
+ gtk_widget_grab_focus (GTK_WIDGET (view));
+
+ if (!found) {
+ gtk_label_set_label (
+ GTK_LABEL (dialog->priv->result_label),
+ N_("No match found"));
+ gtk_widget_show (dialog->priv->result_label);
+ }
+}
+
+static gboolean
+entry_key_release_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GdkEventKey *key = &event->key;
+ EHTMLEditorFindDialog *dialog = user_data;
+
+ if (key->keyval == GDK_KEY_Return) {
+ html_editor_find_dialog_find_cb (dialog);
+ return TRUE;
+ }
+
+ reset_dialog (dialog);
+ return FALSE;
+}
+
+static void
+e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorFindDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_find_dialog_show;
+}
+
+static void
+e_html_editor_find_dialog_init (EHTMLEditorFindDialog *dialog)
+{
+ GtkGrid *main_layout;
+ GtkBox *box;
+ GtkWidget *widget;
+
+ dialog->priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ widget = gtk_entry_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+ dialog->priv->entry = widget;
+ g_signal_connect (
+ widget, "key-release-event",
+ G_CALLBACK (entry_key_release_event), dialog);
+
+ box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
+ gtk_grid_attach (main_layout, GTK_WIDGET (box), 0, 1, 1, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (N_("Search _backwards"));
+ gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+ dialog->priv->backwards = widget;
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (reset_dialog), dialog);
+
+ widget = gtk_check_button_new_with_mnemonic (N_("Case _Sensitive"));
+ gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+ dialog->priv->case_sensitive = widget;
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (reset_dialog), dialog);
+
+ widget = gtk_check_button_new_with_mnemonic (N_("_Wrap Search"));
+ gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+ dialog->priv->wrap_search = widget;
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (reset_dialog), dialog);
+
+ box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
+ gtk_grid_attach (main_layout, GTK_WIDGET (box), 0, 2, 1, 1);
+
+ widget = gtk_label_new ("");
+ gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+ dialog->priv->result_label = widget;
+
+ widget = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_set_spacing (GTK_BOX (widget), 5);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
+ gtk_box_pack_end (box, widget, TRUE, TRUE, 0);
+ box = GTK_BOX (widget);
+
+ box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+ widget = gtk_button_new_from_stock (GTK_STOCK_FIND);
+ gtk_box_pack_start (box, widget, FALSE, FALSE, 5);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_find_dialog_find_cb), dialog);
+ dialog->priv->find_button = widget;
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_find_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_FIND_DIALOG,
+ "editor", editor,
+ "icon-name", GTK_STOCK_FIND,
+ "title", N_("Find"),
+ NULL));
+}
+
+void
+e_html_editor_find_dialog_find_next (EHTMLEditorFindDialog *dialog)
+{
+ if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0) {
+ return;
+ }
+
+ html_editor_find_dialog_find_cb (dialog);
+}
diff --git a/e-util/e-html-editor-find-dialog.h b/e-util/e-html-editor-find-dialog.h
new file mode 100644
index 0000000000..60134f467f
--- /dev/null
+++ b/e-util/e-html-editor-find-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-find-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_FIND_DIALOG_H
+#define E_HTML_EDITOR_FIND_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_FIND_DIALOG \
+ (e_html_editor_find_dialog_get_type ())
+#define E_HTML_EDITOR_FIND_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialog))
+#define E_HTML_EDITOR_FIND_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogClass))
+#define E_IS_HTML_EDITOR_FIND_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG))
+#define E_IS_HTML_EDITOR_FIND_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_FIND_DIALOG))
+#define E_HTML_EDITOR_FIND_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorFindDialog EHTMLEditorFindDialog;
+typedef struct _EHTMLEditorFindDialogClass EHTMLEditorFindDialogClass;
+typedef struct _EHTMLEditorFindDialogPrivate EHTMLEditorFindDialogPrivate;
+
+struct _EHTMLEditorFindDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorFindDialogPrivate *priv;
+};
+
+struct _EHTMLEditorFindDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_find_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_find_dialog_new (EHTMLEditor *editor);
+void e_html_editor_find_dialog_find_next
+ (EHTMLEditorFindDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_FIND_DIALOG_H */
+
diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c
new file mode 100644
index 0000000000..9ace655812
--- /dev/null
+++ b/e-util/e-html-editor-hrule-dialog.c
@@ -0,0 +1,421 @@
+/*
+ * e-html-editor-hrule-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-hrule-dialog.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-view.h"
+
+#include <glib/gi18n-lib.h>
+#include <webkit/webkitdom.h>
+#include <stdlib.h>
+
+#define E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogPrivate))
+
+struct _EHTMLEditorHRuleDialogPrivate {
+ GtkWidget *width_edit;
+ GtkWidget *size_edit;
+ GtkWidget *unit_combo;
+
+ GtkWidget *alignment_combo;
+ GtkWidget *shaded_check;
+
+ WebKitDOMHTMLHRElement *hr_element;
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorHRuleDialog,
+ e_html_editor_hrule_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_hrule_dialog_set_alignment (EHTMLEditorHRuleDialog *dialog)
+{
+ const gchar *alignment;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ alignment = gtk_combo_box_get_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo));
+
+ webkit_dom_htmlhr_element_set_align (dialog->priv->hr_element, alignment);
+}
+
+static void
+html_editor_hrule_dialog_get_alignment (EHTMLEditorHRuleDialog *dialog)
+{
+ gchar *alignment;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ alignment = webkit_dom_htmlhr_element_get_align (dialog->priv->hr_element);
+
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
+ g_free (alignment);
+}
+
+static void
+html_editor_hrule_dialog_set_size (EHTMLEditorHRuleDialog *dialog)
+{
+ gchar *size;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ size = g_strdup_printf (
+ "%d",
+ (gint) gtk_spin_button_get_value (
+ GTK_SPIN_BUTTON (dialog->priv->size_edit)));
+
+ webkit_dom_htmlhr_element_set_size (dialog->priv->hr_element, size);
+
+ g_free (size);
+}
+
+static void
+html_editor_hrule_dialog_get_size (EHTMLEditorHRuleDialog *dialog)
+{
+ gchar *size;
+ gint size_int = 0;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ size = webkit_dom_htmlhr_element_get_size (dialog->priv->hr_element);
+ if (size && *size) {
+ size_int = atoi (size);
+ }
+
+ if (size_int == 0) {
+ size_int = 2;
+ }
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) size_int);
+
+ g_free (size);
+}
+
+static void
+html_editor_hrule_dialog_set_width (EHTMLEditorHRuleDialog *dialog)
+{
+ gchar *width, *units;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ units = gtk_combo_box_text_get_active_text (
+ GTK_COMBO_BOX_TEXT (dialog->priv->unit_combo));
+ width = g_strdup_printf (
+ "%d%s",
+ (gint) gtk_spin_button_get_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+ units);
+
+ webkit_dom_htmlhr_element_set_width (dialog->priv->hr_element, width);
+
+ g_free (units);
+ g_free (width);
+}
+
+static void
+html_editor_hrule_dialog_get_width (EHTMLEditorHRuleDialog *dialog)
+{
+ gchar *width;
+ const gchar *units;
+ gint width_int = 0;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ width = webkit_dom_htmlhr_element_get_width (dialog->priv->hr_element);
+ if (width && *width) {
+ width_int = atoi (width);
+
+ if (strstr (width, "%") != NULL) {
+ units = "units-percent";
+ } else {
+ units = "units-px";
+ }
+ }
+
+ if (width_int == 0) {
+ width_int = 100;
+ units = "units-percent";
+ }
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), (gdouble) width_int);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->unit_combo), units);
+
+ g_free (width);
+}
+
+static void
+html_editor_hrule_dialog_set_shading (EHTMLEditorHRuleDialog *dialog)
+{
+ gboolean no_shade;
+
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ no_shade = !gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->shaded_check));
+
+ webkit_dom_htmlhr_element_set_no_shade (dialog->priv->hr_element, no_shade);
+}
+
+static void
+html_editor_hrule_dialog_get_shading (EHTMLEditorHRuleDialog *dialog)
+{
+ g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->shaded_check),
+ !webkit_dom_htmlhr_element_get_no_shade (dialog->priv->hr_element));
+}
+
+static void
+html_editor_hrule_dialog_hide (GtkWidget *widget)
+{
+ EHTMLEditorHRuleDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (widget);
+
+ priv->hr_element = NULL;
+
+ GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget);
+}
+
+static void
+html_editor_hrule_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorHRuleDialog *dialog;
+ EHTMLEditor *editor;
+ EHTMLEditorSelection *editor_selection;
+ EHTMLEditorView *view;
+
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *selection;
+ WebKitDOMElement *rule;
+
+ dialog = E_HTML_EDITOR_HRULE_DIALOG (widget);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ selection = webkit_dom_dom_window_get_selection (window);
+ if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+ GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->show (widget);
+ return;
+ }
+
+ rule = e_html_editor_view_get_element_under_mouse_click (view);
+ if (!rule) {
+ WebKitDOMElement *caret, *parent, *element;
+
+ caret = e_html_editor_selection_save_caret_position (editor_selection);
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+ element = caret;
+
+ while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ element = parent;
+ parent = webkit_dom_node_get_parent_element (
+ WEBKIT_DOM_NODE (parent));
+ }
+
+ rule = webkit_dom_document_create_element (document, "HR", NULL);
+
+ /* Insert horizontal rule into body below the caret */
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (parent),
+ WEBKIT_DOM_NODE (rule),
+ webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+ NULL);
+
+ e_html_editor_selection_clear_caret_position_marker (editor_selection);
+
+ dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule);
+
+ /* For new rule reset the values to default */
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), 100.0);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->unit_combo), "units-percent");
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->size_edit), 2.0);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), FALSE);
+
+ html_editor_hrule_dialog_set_alignment (dialog);
+ html_editor_hrule_dialog_set_size (dialog);
+ html_editor_hrule_dialog_set_alignment (dialog);
+ html_editor_hrule_dialog_set_shading (dialog);
+
+ e_html_editor_view_set_changed (view, TRUE);
+ } else {
+ dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule);
+
+ html_editor_hrule_dialog_get_alignment (dialog);
+ html_editor_hrule_dialog_get_size (dialog);
+ html_editor_hrule_dialog_get_width (dialog);
+ html_editor_hrule_dialog_get_shading (dialog);
+ }
+
+ /* Chain up to parent implementation */
+ GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_hrule_dialog_class_init (EHTMLEditorHRuleDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorHRuleDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_hrule_dialog_show;
+ widget_class->hide = html_editor_hrule_dialog_hide;
+}
+
+static void
+e_html_editor_hrule_dialog_init (EHTMLEditorHRuleDialog *dialog)
+{
+ GtkGrid *main_layout, *grid;
+ GtkWidget *widget;
+
+ dialog->priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == Size == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Size</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+
+ /* Width */
+ widget = gtk_spin_button_new_with_range (0.0, 100.0, 1.0);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 100);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_hrule_dialog_set_width), dialog);
+ dialog->priv->width_edit = widget;
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+ widget = gtk_label_new_with_mnemonic (_("_Width:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_edit);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-percent");
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_hrule_dialog_set_width), dialog);
+ dialog->priv->unit_combo = widget;
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+ /* Size */
+ widget = gtk_spin_button_new_with_range (0.0, 100.0, 1.0);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 2);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_hrule_dialog_set_size), dialog);
+ dialog->priv->size_edit = widget;
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+
+ widget = gtk_label_new_with_mnemonic (_("_Size:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_edit);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ /* == Style == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Style</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+
+ /* Alignment */
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (
+ GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+ gtk_combo_box_text_append (
+ GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+ gtk_combo_box_text_append (
+ GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "left");
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_hrule_dialog_set_alignment), dialog);
+ dialog->priv->alignment_combo = widget;
+ gtk_grid_attach (grid, widget, 1, 0, 2, 1);
+
+ widget = gtk_label_new_with_mnemonic (_("_Alignment:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), widget);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Shaded */
+ widget = gtk_check_button_new_with_mnemonic (_("S_haded"));
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_hrule_dialog_set_shading), dialog);
+ dialog->priv->shaded_check = widget;
+ gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_hrule_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_HRULE_DIALOG,
+ "editor", editor,
+ "title", _("Rule properties"),
+ NULL));
+}
diff --git a/e-util/e-html-editor-hrule-dialog.h b/e-util/e-html-editor-hrule-dialog.h
new file mode 100644
index 0000000000..876fc2515c
--- /dev/null
+++ b/e-util/e-html-editor-hrule-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-hrule-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_HRULE_DIALOG_H
+#define E_HTML_EDITOR_HRULE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_HRULE_DIALOG \
+ (e_html_editor_hrule_dialog_get_type ())
+#define E_HTML_EDITOR_HRULE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialog))
+#define E_HTML_EDITOR_HRULE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogClass))
+#define E_IS_HTML_EDITOR_HRULE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG))
+#define E_IS_HTML_EDITOR_HRULE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_HRULE_DIALOG))
+#define E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorHRuleDialog EHTMLEditorHRuleDialog;
+typedef struct _EHTMLEditorHRuleDialogClass EHTMLEditorHRuleDialogClass;
+typedef struct _EHTMLEditorHRuleDialogPrivate EHTMLEditorHRuleDialogPrivate;
+
+struct _EHTMLEditorHRuleDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorHRuleDialogPrivate *priv;
+};
+
+struct _EHTMLEditorHRuleDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_hrule_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_hrule_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_HRULE_DIALOG_H */
diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c
new file mode 100644
index 0000000000..346de4419b
--- /dev/null
+++ b/e-util/e-html-editor-image-dialog.c
@@ -0,0 +1,703 @@
+/*
+ * e-html-editor-image-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-image-dialog.h"
+
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+
+#define E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogPrivate))
+
+struct _EHTMLEditorImageDialogPrivate {
+ GtkWidget *file_chooser;
+ GtkWidget *description_edit;
+
+ GtkWidget *width_edit;
+ GtkWidget *width_units;
+ GtkWidget *height_edit;
+ GtkWidget *height_units;
+ GtkWidget *alignment;
+
+ GtkWidget *x_padding_edit;
+ GtkWidget *y_padding_edit;
+ GtkWidget *border_edit;
+
+ GtkWidget *url_edit;
+ GtkWidget *test_url_button;
+
+ WebKitDOMHTMLImageElement *image;
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorImageDialog,
+ e_html_editor_image_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorSelection *editor_selection;
+ EHTMLEditorView *view;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_replace_image_src (
+ editor_selection,
+ WEBKIT_DOM_ELEMENT (dialog->priv->image),
+ gtk_file_chooser_get_uri (
+ GTK_FILE_CHOOSER (dialog->priv->file_chooser)));
+}
+
+static void
+html_editor_image_dialog_set_alt (EHTMLEditorImageDialog *dialog)
+{
+ webkit_dom_html_image_element_set_alt (
+ dialog->priv->image,
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit)));
+}
+
+static void
+html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog)
+{
+ gint requested;
+ gulong natural;
+ gint width;
+
+ natural = webkit_dom_html_image_element_get_natural_width (
+ dialog->priv->image);
+ requested = gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit));
+
+ switch (gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->width_units))) {
+
+ case 0: /* px */
+ width = requested;
+ break;
+
+ case 1: /* percent */
+ width = natural * requested * 0.01;
+ break;
+
+ case 2: /* follow */
+ width = natural;
+ break;
+
+ }
+
+ webkit_dom_html_image_element_set_width (dialog->priv->image, width);
+}
+
+static void
+html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog)
+{
+ gint requested;
+ gulong natural;
+ gint width = 0;
+
+ natural = webkit_dom_html_image_element_get_natural_width (
+ dialog->priv->image);
+ requested = gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit));
+
+ switch (gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->width_units))) {
+
+ case 0: /* px */
+ if (gtk_widget_is_sensitive (dialog->priv->width_edit)) {
+ width = requested * natural * 0.01;
+ } else {
+ width = natural;
+ }
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+ gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+ break;
+
+ case 1: /* percent */
+ if (gtk_widget_is_sensitive (dialog->priv->width_edit)) {
+ width = (((gdouble) requested) / natural) * 100;
+ } else {
+ width = 100;
+ }
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+ gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+ break;
+
+ case 2: /* follow */
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image),
+ "style",
+ "width: auto;",
+ NULL);
+ gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
+ break;
+ }
+
+ if (width != 0) {
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), width);
+ }
+}
+
+static void
+html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog)
+{
+ gint requested;
+ gulong natural;
+ gint height;
+
+ natural = webkit_dom_html_image_element_get_natural_height (
+ dialog->priv->image);
+ requested = gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->height_edit));
+
+ switch (gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->height_units))) {
+
+ case 0: /* px */
+ height = requested;
+ break;
+
+ case 1: /* percent */
+ height = natural * requested * 0.01;
+ break;
+
+ case 2: /* follow */
+ height = natural;
+ break;
+
+ }
+
+ webkit_dom_html_image_element_set_height (dialog->priv->image, height);
+}
+
+static void
+html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog)
+{
+ gint requested;
+ gulong natural;
+ gint height = -1;
+
+ natural = webkit_dom_html_image_element_get_natural_height (
+ dialog->priv->image);
+ requested = gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->height_edit));
+
+ switch (gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->height_units))) {
+
+ case 0: /* px */
+ if (gtk_widget_is_sensitive (dialog->priv->height_edit)) {
+ height = requested * natural * 0.01;
+ } else {
+ height = natural;
+ }
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+ gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
+ break;
+
+ case 1: /* percent */
+ if (gtk_widget_is_sensitive (dialog->priv->height_edit)) {
+ height = (((gdouble) requested) / natural) * 100;
+ } else {
+ height = 100;
+ }
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+ gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
+ break;
+
+ case 2: /* follow */
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image),
+ "style",
+ "height: auto;",
+ NULL);
+ gtk_widget_set_sensitive (dialog->priv->height_edit, FALSE);
+ break;
+ }
+
+ if (height != -1) {
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->height_edit), height);
+ }
+}
+
+static void
+html_editor_image_dialog_set_alignment (EHTMLEditorImageDialog *dialog)
+{
+ webkit_dom_html_image_element_set_align (
+ dialog->priv->image,
+ gtk_combo_box_get_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment)));
+}
+
+static void
+html_editor_image_dialog_set_x_padding (EHTMLEditorImageDialog *dialog)
+{
+ webkit_dom_html_image_element_set_hspace (
+ dialog->priv->image,
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->x_padding_edit)));
+}
+
+static void
+html_editor_image_dialog_set_y_padding (EHTMLEditorImageDialog *dialog)
+{
+ webkit_dom_html_image_element_set_vspace (
+ dialog->priv->image,
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->y_padding_edit)));
+}
+
+static void
+html_editor_image_dialog_set_border (EHTMLEditorImageDialog *dialog)
+{
+ gchar *val;
+
+ val = g_strdup_printf (
+ "%d", gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+
+ webkit_dom_html_image_element_set_border (dialog->priv->image, val);
+
+ g_free (val);
+}
+
+static void
+html_editor_image_dialog_set_url (EHTMLEditorImageDialog *dialog)
+{
+ WebKitDOMElement *link;
+ const gchar *url;
+
+ url = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit));
+ link = e_html_editor_dom_node_find_parent_element (
+ WEBKIT_DOM_NODE (dialog->priv->image), "A");
+
+ if (link) {
+ if (!url || !*url) {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (link)),
+ WEBKIT_DOM_NODE (dialog->priv->image),
+ WEBKIT_DOM_NODE (link), NULL);
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (link)),
+ WEBKIT_DOM_NODE (link), NULL);
+ } else {
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+ }
+ } else {
+ if (url && *url) {
+ WebKitDOMDocument *document;
+
+ document = webkit_dom_node_get_owner_document (
+ WEBKIT_DOM_NODE (dialog->priv->image));
+ link = webkit_dom_document_create_element (
+ document, "A", NULL);
+
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (dialog->priv->image)),
+ WEBKIT_DOM_NODE (link),
+ WEBKIT_DOM_NODE (dialog->priv->image), NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (link),
+ WEBKIT_DOM_NODE (dialog->priv->image), NULL);
+ }
+ }
+}
+
+static void
+html_editor_image_dialog_test_url (EHTMLEditorImageDialog *dialog)
+{
+ gtk_show_uri (
+ gtk_window_get_screen (GTK_WINDOW (dialog)),
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
+ GDK_CURRENT_TIME,
+ NULL);
+}
+
+static void
+html_editor_image_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorImageDialog *dialog;
+ WebKitDOMElement *link;
+ gchar *tmp;
+ glong val;
+
+ dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
+
+ if (!dialog->priv->image) {
+ return;
+ }
+
+ tmp = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->image), "data-uri");
+ if (tmp && *tmp) {
+ gtk_file_chooser_set_uri (
+ GTK_FILE_CHOOSER (dialog->priv->file_chooser), tmp);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (dialog->priv->file_chooser), TRUE);
+ g_free (tmp);
+ } else {
+ gtk_file_chooser_set_uri (
+ GTK_FILE_CHOOSER (dialog->priv->file_chooser), "");
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (dialog->priv->file_chooser), FALSE);
+ }
+
+ tmp = webkit_dom_html_image_element_get_alt (dialog->priv->image);
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->description_edit), tmp ? tmp : "");
+ g_free (tmp);
+
+ val = webkit_dom_html_image_element_get_width (dialog->priv->image);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
+
+ val = webkit_dom_html_image_element_get_height (dialog->priv->image);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->height_edit), val);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->height_units), "units-px");
+
+ tmp = webkit_dom_html_image_element_get_border (dialog->priv->image);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment),
+ (tmp && *tmp) ? tmp : "bottom");
+ g_free (tmp);
+
+ val = webkit_dom_html_image_element_get_hspace (dialog->priv->image);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->x_padding_edit), val);
+
+ val = webkit_dom_html_image_element_get_vspace (dialog->priv->image);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), val);
+
+ link = e_html_editor_dom_node_find_parent_element (
+ WEBKIT_DOM_NODE (dialog->priv->image), "A");
+ if (link) {
+ tmp = webkit_dom_html_anchor_element_get_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link));
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), tmp);
+ g_free (tmp);
+ }
+
+ /* Chain up to parent implementation */
+ GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_image_dialog_hide (GtkWidget *widget)
+{
+ EHTMLEditorImageDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (widget);
+
+ priv->image = NULL;
+
+ GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
+}
+
+static void
+e_html_editor_image_dialog_class_init (EHTMLEditorImageDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorImageDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_image_dialog_show;
+ widget_class->hide = html_editor_image_dialog_hide;
+}
+
+static void
+e_html_editor_image_dialog_init (EHTMLEditorImageDialog *dialog)
+{
+ GtkGrid *main_layout, *grid;
+ GtkWidget *widget;
+ GtkFileFilter *file_filter;
+
+ dialog->priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == General == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Source */
+ widget = e_image_chooser_dialog_new (
+ _("Choose Background Image"),
+ GTK_WINDOW (dialog));
+ gtk_file_chooser_set_action (
+ GTK_FILE_CHOOSER (widget), GTK_FILE_CHOOSER_ACTION_OPEN);
+
+ file_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (file_filter, _("Images"));
+ gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+ widget = gtk_file_chooser_button_new_with_dialog (widget);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "file-set",
+ G_CALLBACK (html_editor_image_dialog_set_src), dialog);
+ dialog->priv->file_chooser = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Source:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->file_chooser);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Description */
+ widget = gtk_entry_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "notify::text",
+ G_CALLBACK (html_editor_image_dialog_set_alt), dialog);
+ dialog->priv->description_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Description:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->description_edit);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ /* == Layout == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Width */
+ widget = gtk_spin_button_new_with_range (1, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_image_dialog_set_width), dialog);
+ dialog->priv->width_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Width:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->width_edit);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-follow", "follow");
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-px");
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_image_dialog_set_width_units), dialog);
+ dialog->priv->width_units = widget;
+
+ /* Height */
+ widget = gtk_spin_button_new_with_range (1, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_image_dialog_set_height), dialog);
+ dialog->priv->height_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Height:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->height_edit);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-follow", "follow");
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-px");
+ gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_image_dialog_set_height_units), dialog);
+ dialog->priv->height_units = widget;
+
+ /* Alignment */
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "top", _("Top"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "middle", _("Middle"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "bottom", _("Bottom"));
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "bottom");
+ gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_image_dialog_set_alignment), dialog);
+ dialog->priv->alignment = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Alignment"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->alignment);
+ gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+ /* X Padding */
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_image_dialog_set_x_padding), dialog);
+ dialog->priv->x_padding_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_X-Padding:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->x_padding_edit);
+ gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 0, 1, 1);
+
+ /* Y Padding */
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 5, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_image_dialog_set_y_padding), dialog);
+ dialog->priv->y_padding_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Y-Padding:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->y_padding_edit);
+ gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 1, 1, 1);
+
+ /* Border */
+ widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+ gtk_grid_attach (grid, widget, 5, 2, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_image_dialog_set_border), dialog);
+ dialog->priv->border_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Border:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->border_edit);
+ gtk_grid_attach (grid, widget, 4, 2, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 2, 1, 1);
+
+ /* == Layout == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Link</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ widget = gtk_entry_new ();
+ gtk_grid_attach (grid, widget, 1 ,0, 1, 1);
+ gtk_widget_set_hexpand (widget, TRUE);
+ g_signal_connect_swapped (
+ widget, "notify::text",
+ G_CALLBACK (html_editor_image_dialog_set_url), dialog);
+ dialog->priv->url_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_URL:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->url_edit);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ widget = gtk_button_new_with_mnemonic (_("_Test URL..."));
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_image_dialog_test_url), dialog);
+ dialog->priv->test_url_button = widget;
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_image_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_IMAGE_DIALOG,
+ "editor", editor,
+ "title", N_("Image Properties"),
+ NULL));
+}
+
+void
+e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
+ WebKitDOMNode *image)
+{
+ EHTMLEditorImageDialogClass *class;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_IMAGE_DIALOG (dialog));
+
+ if (image) {
+ dialog->priv->image = WEBKIT_DOM_HTML_IMAGE_ELEMENT (image);
+ } else {
+ dialog->priv->image = NULL;
+ }
+
+ class = E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS (dialog);
+ GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
+}
diff --git a/e-util/e-html-editor-image-dialog.h b/e-util/e-html-editor-image-dialog.h
new file mode 100644
index 0000000000..efdbaf963c
--- /dev/null
+++ b/e-util/e-html-editor-image-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-image-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_IMAGE_DIALOG_H
+#define E_HTML_EDITOR_IMAGE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_IMAGE_DIALOG \
+ (e_html_editor_image_dialog_get_type ())
+#define E_HTML_EDITOR_IMAGE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialog))
+#define E_HTML_EDITOR_IMAGE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogClass))
+#define E_IS_HTML_EDITOR_IMAGE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG))
+#define E_IS_HTML_EDITOR_IMAGE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_IMAGE_DIALOG))
+#define E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorImageDialog EHTMLEditorImageDialog;
+typedef struct _EHTMLEditorImageDialogClass EHTMLEditorImageDialogClass;
+typedef struct _EHTMLEditorImageDialogPrivate EHTMLEditorImageDialogPrivate;
+
+struct _EHTMLEditorImageDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorImageDialogPrivate *priv;
+};
+
+struct _EHTMLEditorImageDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_image_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_image_dialog_new (EHTMLEditor *editor);
+void e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
+ WebKitDOMNode *image);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_IMAGE_DIALOG_H */
+
diff --git a/e-util/e-html-editor-link-dialog.c b/e-util/e-html-editor-link-dialog.c
new file mode 100644
index 0000000000..0572d07c09
--- /dev/null
+++ b/e-util/e-html-editor-link-dialog.c
@@ -0,0 +1,390 @@
+/*
+ * e-html-editor-link-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-link-dialog.h"
+#include "e-html-editor-selection.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-view.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogPrivate))
+
+G_DEFINE_TYPE (
+ EHTMLEditorLinkDialog,
+ e_html_editor_link_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorLinkDialogPrivate {
+ GtkWidget *url_edit;
+ GtkWidget *label_edit;
+ GtkWidget *test_button;
+
+ GtkWidget *remove_link_button;
+ GtkWidget *ok_button;
+
+ gboolean label_autofill;
+};
+
+static void
+html_editor_link_dialog_test_link (EHTMLEditorLinkDialog *dialog)
+{
+ gtk_show_uri (
+ gtk_window_get_screen (GTK_WINDOW (dialog)),
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
+ GDK_CURRENT_TIME,
+ NULL);
+}
+
+static void
+html_editor_link_dialog_url_changed (EHTMLEditorLinkDialog *dialog)
+{
+ if (dialog->priv->label_autofill &&
+ gtk_widget_is_sensitive (dialog->priv->label_edit)) {
+ const gchar *text;
+
+ text = gtk_entry_get_text (
+ GTK_ENTRY (dialog->priv->url_edit));
+ gtk_entry_set_text (
+ GTK_ENTRY (dialog->priv->label_edit), text);
+ }
+}
+
+static gboolean
+html_editor_link_dialog_description_changed (EHTMLEditorLinkDialog *dialog)
+{
+ const gchar *text;
+
+ text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit));
+ dialog->priv->label_autofill = (*text == '\0');
+
+ return FALSE;
+}
+
+static void
+html_editor_link_dialog_remove_link (EHTMLEditorLinkDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ e_html_editor_selection_unlink (selection);
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+static void
+html_editor_link_dialog_ok (EHTMLEditorLinkDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range;
+ WebKitDOMElement *link;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+
+ if (!dom_selection ||
+ (webkit_dom_dom_selection_get_range_count (dom_selection) == 0)) {
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ return;
+ }
+
+ range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ link = e_html_editor_dom_node_find_parent_element (
+ webkit_dom_range_get_start_container (range, NULL), "A");
+ if (!link) {
+ if ((webkit_dom_range_get_start_container (range, NULL) !=
+ webkit_dom_range_get_end_container (range, NULL)) ||
+ (webkit_dom_range_get_start_offset (range, NULL) !=
+ webkit_dom_range_get_end_offset (range, NULL))) {
+
+ WebKitDOMDocumentFragment *fragment;
+ fragment = webkit_dom_range_extract_contents (range, NULL);
+ link = e_html_editor_dom_node_find_child_element (
+ WEBKIT_DOM_NODE (fragment), "A");
+ webkit_dom_range_insert_node (
+ range, WEBKIT_DOM_NODE (fragment), NULL);
+
+ webkit_dom_dom_selection_set_base_and_extent (
+ dom_selection,
+ webkit_dom_range_get_start_container (range, NULL),
+ webkit_dom_range_get_start_offset (range, NULL),
+ webkit_dom_range_get_end_container (range, NULL),
+ webkit_dom_range_get_end_offset (range, NULL),
+ NULL);
+ } else {
+ /* get element that was clicked on */
+ link = e_html_editor_view_get_element_under_mouse_click (view);
+ if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+ link = NULL;
+ }
+ }
+
+ if (link) {
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link),
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)));
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (link),
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)),
+ NULL);
+ } else {
+ gchar *text;
+
+ /* Check whether a text is selected or not */
+ text = webkit_dom_range_get_text (range);
+ if (text && *text) {
+ e_html_editor_selection_create_link (
+ selection,
+ gtk_entry_get_text (
+ GTK_ENTRY (dialog->priv->url_edit)));
+ } else {
+ gchar *html = g_strdup_printf (
+ "<a href=\"%s\">%s</a>",
+ gtk_entry_get_text (
+ GTK_ENTRY (dialog->priv->url_edit)),
+ gtk_entry_get_text (
+ GTK_ENTRY (dialog->priv->label_edit)));
+
+ e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html);
+
+ g_free (html);
+
+ }
+
+ g_free (text);
+ }
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+static gboolean
+html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog,
+ GdkEventKey *event)
+{
+ /* We can't do thins in key_released, because then you could not open
+ * this dialog from main menu by pressing enter on Insert->Link action */
+ if (event->keyval == GDK_KEY_Return) {
+ html_editor_link_dialog_ok (dialog);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+html_editor_link_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorLinkDialog *dialog;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range;
+ WebKitDOMElement *link;
+
+ dialog = E_HTML_EDITOR_LINK_DIALOG (widget);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+
+ 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);
+
+ /* Reset to default values */
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), "http://");
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->label_edit), "");
+ gtk_widget_set_sensitive (dialog->priv->label_edit, TRUE);
+ gtk_widget_set_sensitive (dialog->priv->remove_link_button, TRUE);
+ dialog->priv->label_autofill = TRUE;
+
+ /* No selection at all */
+ if (!dom_selection ||
+ webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+ gtk_widget_set_sensitive (dialog->priv->remove_link_button, FALSE);
+ goto chainup;
+ }
+
+ range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ link = e_html_editor_dom_node_find_parent_element (
+ webkit_dom_range_get_start_container (range, NULL), "A");
+ if (!link) {
+ if ((webkit_dom_range_get_start_container (range, NULL) !=
+ webkit_dom_range_get_end_container (range, NULL)) ||
+ (webkit_dom_range_get_start_offset (range, NULL) !=
+ webkit_dom_range_get_end_offset (range, NULL))) {
+
+ WebKitDOMDocumentFragment *fragment;
+ fragment = webkit_dom_range_clone_contents (range, NULL);
+ link = e_html_editor_dom_node_find_child_element (
+ WEBKIT_DOM_NODE (fragment), "A");
+ } else {
+ /* get element that was clicked on */
+ link = e_html_editor_view_get_element_under_mouse_click (view);
+ if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+ link = NULL;
+ }
+ }
+
+ if (link) {
+ gchar *href, *text;
+
+ href = webkit_dom_html_anchor_element_get_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link));
+ text = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (link));
+
+ gtk_entry_set_text (
+ GTK_ENTRY (dialog->priv->url_edit), href);
+ gtk_entry_set_text (
+ GTK_ENTRY (dialog->priv->label_edit), text);
+
+ g_free (text);
+ g_free (href);
+ } else {
+ gchar *text;
+
+ text = webkit_dom_range_get_text (range);
+ if (text && *text) {
+ gtk_entry_set_text (
+ GTK_ENTRY (dialog->priv->label_edit), text);
+ gtk_widget_set_sensitive (
+ dialog->priv->label_edit, FALSE);
+ gtk_widget_set_sensitive (
+ dialog->priv->remove_link_button, FALSE);
+ }
+ g_free (text);
+ }
+
+ chainup:
+ /* Chain up to parent implementation */
+ GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_link_dialog_class_init (EHTMLEditorLinkDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorLinkDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_link_dialog_show;
+}
+
+static void
+e_html_editor_link_dialog_init (EHTMLEditorLinkDialog *dialog)
+{
+ GtkGrid *main_layout;
+ GtkBox *button_box;
+ GtkWidget *widget;
+
+ dialog->priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ widget = gtk_entry_new ();
+ gtk_grid_attach (main_layout, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "notify::text",
+ G_CALLBACK (html_editor_link_dialog_url_changed), dialog);
+ g_signal_connect_swapped (
+ widget, "key-press-event",
+ G_CALLBACK (html_editor_link_dialog_entry_key_pressed), dialog);
+ dialog->priv->url_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_URL:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->url_edit);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ widget = gtk_button_new_with_mnemonic (_("_Test URL..."));
+ gtk_grid_attach (main_layout, widget, 2, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_link_dialog_test_link), dialog);
+ dialog->priv->test_button = widget;
+
+ widget = gtk_entry_new ();
+ gtk_grid_attach (main_layout, widget, 1, 1, 2, 1);
+ g_signal_connect_swapped (
+ widget, "key-release-event",
+ G_CALLBACK (html_editor_link_dialog_description_changed), dialog);
+ g_signal_connect_swapped (
+ widget, "key-press-event",
+ G_CALLBACK (html_editor_link_dialog_entry_key_pressed), dialog);
+ dialog->priv->label_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Description:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->label_edit);
+ gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+ button_box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+
+ widget = gtk_button_new_with_mnemonic (_("_Remove Link"));
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_link_dialog_remove_link), dialog);
+ gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+ dialog->priv->remove_link_button = widget;
+
+ widget = gtk_button_new_from_stock (GTK_STOCK_OK);
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_link_dialog_ok), dialog);
+ gtk_box_pack_end (button_box, widget, FALSE, FALSE, 5);
+ dialog->priv->ok_button = widget;
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_link_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_LINK_DIALOG,
+ "editor", editor,
+ "icon-name", "insert-link",
+ "title", N_("Link Properties"),
+ NULL));
+}
diff --git a/e-util/e-html-editor-link-dialog.h b/e-util/e-html-editor-link-dialog.h
new file mode 100644
index 0000000000..a1e426fe01
--- /dev/null
+++ b/e-util/e-html-editor-link-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-link-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_LINK_DIALOG_H
+#define E_HTML_EDITOR_LINK_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_LINK_DIALOG \
+ (e_html_editor_link_dialog_get_type ())
+#define E_HTML_EDITOR_LINK_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialog))
+#define E_HTML_EDITOR_LINK_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogClass))
+#define E_IS_HTML_EDITOR_LINK_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG))
+#define E_IS_HTML_EDITOR_LINK_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_LINK_DIALOG))
+#define E_HTML_EDITOR_LINK_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorLinkDialog EHTMLEditorLinkDialog;
+typedef struct _EHTMLEditorLinkDialogClass EHTMLEditorLinkDialogClass;
+typedef struct _EHTMLEditorLinkDialogPrivate EHTMLEditorLinkDialogPrivate;
+
+struct _EHTMLEditorLinkDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorLinkDialogPrivate *priv;
+};
+
+struct _EHTMLEditorLinkDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_link_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_link_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_LINK_DIALOG_H */
diff --git a/e-util/e-html-editor-manager.ui b/e-util/e-html-editor-manager.ui
new file mode 100644
index 0000000000..75f2c34b20
--- /dev/null
+++ b/e-util/e-html-editor-manager.ui
@@ -0,0 +1,181 @@
+<ui>
+ <menubar name='main-menu'>
+ <placeholder name='pre-file-menu'/>
+ <menu action='file-menu'/>
+ <placeholder name='pre-edit-menu'/>
+ <menu action='edit-menu'>
+ <placeholder name='edit-menu-top'/>
+ <menuitem action='undo'/>
+ <menuitem action='redo'/>
+ <separator/>
+ <menuitem action='cut'/>
+ <menuitem action='copy'/>
+ <menuitem action='paste'/>
+ <menuitem action='paste-quote'/>
+ <separator/>
+ <menuitem action='select-all'/>
+ <separator/>
+ <menuitem action='show-find'/>
+ <menuitem action='find-again'/>
+ <menuitem action='show-replace'/>
+ <separator/>
+ <placeholder name='pre-spell-check'/>
+ <menuitem action='spell-check'/>
+ <menu action='language-menu'/>
+ </menu>
+ <placeholder name='pre-insert-menu'>
+ <menu action='view-menu'>
+ <placeholder name='view-menu-top'/>
+ <menuitem action='webkit-inspector'/>
+ <separator/>
+ </menu>
+ </placeholder>
+ <menu action='insert-menu'>
+ <placeholder name='insert-menu-top'/>
+ <menuitem action='insert-image'/>
+ <menuitem action='insert-link'/>
+ <menuitem action='insert-rule'/>
+ <menuitem action='insert-table'/>
+ <menuitem action='insert-text-file'/>
+ <menuitem action='insert-html-file'/>
+ <menuitem action='insert-emoticon'/>
+ </menu>
+ <placeholder name='pre-format-menu'/>
+ <menu action='format-menu'>
+ <placeholder name='format-menu-top'/>
+ <menuitem action='mode-html'/>
+ <menuitem action='mode-plain'/>
+ <separator/>
+ <menu action='font-style-menu'>
+ <menuitem action='monospaced'/>
+ <separator/>
+ <menuitem action='bold'/>
+ <menuitem action='italic'/>
+ <menuitem action='underline'/>
+ <menuitem action='strikethrough'/>
+ </menu>
+ <menu action='font-size-menu'>
+ <menuitem action='size-minus-two'/>
+ <menuitem action='size-minus-one'/>
+ <menuitem action='size-plus-zero'/>
+ <menuitem action='size-plus-one'/>
+ <menuitem action='size-plus-two'/>
+ <menuitem action='size-plus-three'/>
+ <menuitem action='size-plus-four'/>
+ </menu>
+ <separator/>
+ <menu action='paragraph-style-menu'>
+ <menuitem action='style-normal'/>
+ <separator/>
+ <menuitem action='style-h1'/>
+ <menuitem action='style-h2'/>
+ <menuitem action='style-h3'/>
+ <menuitem action='style-h4'/>
+ <menuitem action='style-h5'/>
+ <menuitem action='style-h6'/>
+ <separator/>
+ <menuitem action='style-list-bullet'/>
+ <menuitem action='style-list-roman'/>
+ <menuitem action='style-list-number'/>
+ <menuitem action='style-list-alpha'/>
+ <separator/>
+ <menuitem action='style-address'/>
+ <menuitem action='style-preformat'/>
+ </menu>
+ <menu action='justify-menu'>
+ <menuitem action='justify-left'/>
+ <menuitem action='justify-center'/>
+ <menuitem action='justify-right'/>
+ </menu>
+ <separator/>
+ <menuitem action='indent'/>
+ <menuitem action='unindent'/>
+ <menuitem action='wrap-lines'/>
+ <separator/>
+ <menuitem action='properties-page'/>
+ </menu>
+ </menubar>
+ <toolbar name='main-toolbar'>
+ <placeholder name='pre-main-toolbar'/>
+ <toolitem action='undo'/>
+ <toolitem action='redo'/>
+ <separator/>
+ <toolitem action='cut'/>
+ <toolitem action='copy'/>
+ <toolitem action='paste'/>
+ <separator/>
+ <toolitem action='show-find'/>
+ <toolitem action='show-replace'/>
+ </toolbar>
+ <toolbar name='edit-toolbar'>
+ <separator/>
+ <toolitem action='justify-left'/>
+ <toolitem action='justify-center'/>
+ <toolitem action='justify-right'/>
+ <separator/>
+ <toolitem action='unindent'/>
+ <toolitem action='indent'/>
+ </toolbar>
+ <toolbar name='html-toolbar'>
+ <separator/>
+ <toolitem action='monospaced'/>
+ <toolitem action='bold'/>
+ <toolitem action='italic'/>
+ <toolitem action='underline'/>
+ <toolitem action='strikethrough'/>
+ <separator/>
+ <toolitem action='insert-image'/>
+ <toolitem action='insert-link'/>
+ <toolitem action='insert-rule'/>
+ <toolitem action='insert-table'/>
+ <toolitem action='insert-emoticon'/>
+ </toolbar>
+ <popup name='context-menu'>
+ <placeholder name='context-spell-suggest'/>
+ <menu action='context-more-suggestions-menu'/>
+ <separator/>
+ <menuitem action='context-spell-ignore'/>
+ <menu action='context-spell-add-menu'/>
+ <menuitem action='context-spell-add'/>
+ <separator/>
+ <menuitem action='undo'/>
+ <menuitem action='redo'/>
+ <separator/>
+ <menuitem action='cut'/>
+ <menuitem action='copy'/>
+ <menuitem action='paste'/>
+ <menuitem action='paste-quote'/>
+ <separator/>
+ <menuitem action='context-insert-link'/>
+ <menuitem action='context-remove-link'/>
+ <separator/>
+ <menu action='context-properties-menu'>
+ <menuitem action='context-properties-text'/>
+ <menuitem action='context-properties-link'/>
+ <menuitem action='context-properties-rule'/>
+ <menuitem action='context-properties-image'/>
+ <menuitem action='context-properties-paragraph'/>
+ <menuitem action='context-properties-cell'/>
+ <menuitem action='context-properties-table'/>
+ <menuitem action='context-properties-page'/>
+ </menu>
+ <separator/>
+ <menu action='context-insert-table-menu'>
+ <menuitem action='context-insert-table'/>
+ <separator/>
+ <menuitem action='context-insert-row-above'/>
+ <menuitem action='context-insert-row-below'/>
+ <separator/>
+ <menuitem action='context-insert-column-before'/>
+ <menuitem action='context-insert-column-after'/>
+ </menu>
+ <menu action='context-delete-table-menu'>
+ <menuitem action='context-delete-table'/>
+ <menuitem action='context-delete-row'/>
+ <menuitem action='context-delete-column'/>
+ <menuitem action='context-delete-cell'/>
+ </menu>
+ <separator/>
+ <menu action='context-input-methods-menu'/>
+ </popup>
+</ui>
diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c
new file mode 100644
index 0000000000..53c8fce8c7
--- /dev/null
+++ b/e-util/e-html-editor-page-dialog.c
@@ -0,0 +1,513 @@
+/*
+ * e-html-editor-page-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-page-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogPrivate))
+
+struct _EHTMLEditorPageDialogPrivate {
+ GtkWidget *text_color_picker;
+ GtkWidget *link_color_picker;
+ GtkWidget *background_color_picker;
+
+ GtkWidget *background_template_combo;
+ GtkWidget *background_image_filechooser;
+};
+
+typedef struct _Template {
+ const gchar *name;
+ const gchar *filename;
+ GdkRGBA text_color;
+ GdkRGBA link_color;
+ GdkRGBA background_color;
+ gint left_margin;
+} Template;
+
+static const Template templates[] = {
+
+ {
+ N_("None"),
+ NULL,
+ { 0.0, 0.0 , 0.0 , 1 },
+ { 0.3, 0.55, 0.85, 1 },
+ { 1.0, 1.0 , 1.0 , 1 },
+ 0
+ },
+
+ {
+ N_("Perforated Paper"),
+ "paper.png",
+ { 0.0, 0.0, 0.0, 1 },
+ { 0.0, 0.2, 0.4, 1 },
+ { 1.0, 1.0, 1.0, 0 },
+ 30
+ },
+
+ {
+ N_("Blue Ink"),
+ "texture.png",
+ { 0.1, 0.12, 0.56, 1 },
+ { 0.0, 0.0, 1.0, 1 },
+ { 1.0, 1.0, 1.0, 1 },
+ 0
+ },
+
+ {
+ N_("Paper"),
+ "rect.png",
+ { 0, 0, 0, 1 },
+ { 0, 0, 1, 1 },
+ { 1, 1, 1, 1 },
+ 0
+ },
+
+ {
+ N_("Ribbon"),
+ "ribbon.jpg",
+ { 0.0, 0.0, 0.0, 1 },
+ { 0.6, 0.2, 0.4, 1 },
+ { 1.0, 1.0, 1.0, 1 },
+ 70
+ },
+
+ {
+ N_("Midnight"),
+ "midnight-stars.jpg",
+ { 1.0, 1.0, 1.0, 1 },
+ { 1.0, 0.6, 0.0, 1 },
+ { 0.0, 0.0, 0.0, 1 },
+ 0
+ },
+
+ {
+ N_("Confidential"),
+ "confidential-stamp.jpg",
+ { 0.0, 0.0, 0.0, 1 },
+ { 0.0, 0.0, 1.0, 1 },
+ { 1.0, 1.0, 1.0, 1 },
+ 0
+ },
+
+ {
+ N_("Draft"),
+ "draft-stamp.jpg",
+ { 0.0, 0.0, 0.0, 1 },
+ { 0.0, 0.0, 1.0, 1 },
+ { 1.0, 1.0, 1.0, 1 },
+ 0
+ },
+
+ {
+ N_("Graph Paper"),
+ "draft-paper.png",
+ { 0.0 , 0.0 , 0.5, 1 },
+ { 0.88, 0.13, 0.14, 1 },
+ { 1.0 , 1.0 , 1.0 , 1 },
+ 0
+ }
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorPageDialog,
+ e_html_editor_page_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_page_dialog_set_text_color (EHTMLEditorPageDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ GdkRGBA rgba;
+ gchar *color;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
+
+ color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+ webkit_dom_html_body_element_set_text (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+ g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_link_color (EHTMLEditorPageDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ GdkRGBA rgba;
+ gchar *color;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
+
+ color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+ webkit_dom_html_body_element_set_link (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+ g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_background_color (EHTMLEditorPageDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ GdkRGBA rgba;
+ gchar *color;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+
+ color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+
+ webkit_dom_html_body_element_set_bg_color (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+ g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_background_from_template (EHTMLEditorPageDialog *dialog)
+{
+ const Template *tmplt;
+
+ tmplt = &templates[
+ gtk_combo_box_get_active (
+ GTK_COMBO_BOX (dialog->priv->background_template_combo))];
+
+ /* Special case - 'none' template */
+ if (tmplt->filename == NULL) {
+ gtk_file_chooser_unselect_all (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser));
+ } else {
+ gchar *filename;
+
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->text_color_picker),
+ &tmplt->text_color);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker),
+ &tmplt->background_color);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->link_color_picker),
+ &tmplt->link_color);
+
+ filename = g_build_filename (EVOLUTION_IMAGESDIR, tmplt->filename, NULL);
+
+ gtk_file_chooser_set_filename (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser),
+ filename);
+ g_free (filename);
+ }
+
+}
+
+static void
+html_editor_page_dialog_set_background_image (EHTMLEditorPageDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ gchar *uri;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ uri = gtk_file_chooser_get_uri (
+ GTK_FILE_CHOOSER (
+ dialog->priv->background_image_filechooser));
+
+ webkit_dom_html_body_element_set_background (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body), uri ? uri : "");
+
+ g_free (uri);
+}
+
+static void
+html_editor_page_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorPageDialog *dialog;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ gchar *tmp;
+ GdkRGBA rgba;
+
+ dialog = E_HTML_EDITOR_PAGE_DIALOG (widget);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ tmp = webkit_dom_html_body_element_get_background (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+ if (tmp && *tmp) {
+ gint ii;
+ gchar *fname = g_filename_from_uri (tmp, NULL, NULL);
+ for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) {
+ const Template *tmplt = &templates[ii];
+
+ if (g_strcmp0 (tmplt->filename, fname) == 0) {
+ gtk_combo_box_set_active (
+ GTK_COMBO_BOX (dialog->priv->background_template_combo),
+ ii);
+ break;
+ }
+ }
+ g_free (fname);
+ } else {
+ gtk_combo_box_set_active (
+ GTK_COMBO_BOX (dialog->priv->background_template_combo), 0);
+ }
+ g_free (tmp);
+
+ tmp = webkit_dom_html_body_element_get_text (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+ if (!tmp || !*tmp) {
+ GdkColor *color;
+ GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view));
+ color = &style->text[GTK_STATE_NORMAL];
+
+ rgba.alpha = 1;
+ rgba.red = ((gdouble) color->red) / G_MAXUINT16;
+ rgba.green = ((gdouble) color->green) / G_MAXUINT16;
+ rgba.blue = ((gdouble) color->blue) / G_MAXUINT16;
+ } else {
+ gdk_rgba_parse (&rgba, tmp);
+ }
+ g_free (tmp);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
+
+ tmp = webkit_dom_html_body_element_get_link (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+ if (!tmp || !*tmp) {
+ GdkColor color;
+ gtk_widget_style_get (
+ GTK_WIDGET (view), "link-color", &color, NULL);
+
+ rgba.alpha = 1;
+ rgba.red = ((gdouble) color.red) / G_MAXUINT16;
+ rgba.green = ((gdouble) color.green) / G_MAXUINT16;
+ rgba.blue = ((gdouble) color.blue) / G_MAXUINT16;
+ } else {
+ gdk_rgba_parse (&rgba, tmp);
+ }
+ g_free (tmp);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
+
+ tmp = webkit_dom_html_body_element_get_bg_color (
+ WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+ if (!tmp || !*tmp) {
+ GdkColor *color;
+ GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view));
+ color = &style->base[GTK_STATE_NORMAL];
+
+ rgba.alpha = 1;
+ rgba.red = ((gdouble) color->red) / G_MAXUINT16;
+ rgba.green = ((gdouble) color->green) / G_MAXUINT16;
+ rgba.blue = ((gdouble) color->blue) / G_MAXUINT16;
+
+ } else {
+ gdk_rgba_parse (&rgba, tmp);
+ }
+ g_free (tmp);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+
+ GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorPageDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_page_dialog_show;
+}
+
+static void
+e_html_editor_page_dialog_init (EHTMLEditorPageDialog *dialog)
+{
+ GtkGrid *grid, *main_layout;
+ GtkWidget *widget;
+ gint ii;
+
+ dialog->priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == Colors == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Colors</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Text */
+ widget = e_color_combo_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_page_dialog_set_text_color), dialog);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->text_color_picker = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Text:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->text_color_picker);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Link */
+ widget = e_color_combo_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_page_dialog_set_link_color), dialog);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ dialog->priv->link_color_picker = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Link:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->link_color_picker);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ /* Background */
+ widget = e_color_combo_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_page_dialog_set_background_color), dialog);
+ gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+ dialog->priv->background_color_picker = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Background:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_color_picker);
+ gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+ /* == Background Image == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background Image</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Template */
+ widget = gtk_combo_box_text_new ();
+ for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) {
+ gtk_combo_box_text_append_text (
+ GTK_COMBO_BOX_TEXT (widget), templates[ii].name);
+ }
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_page_dialog_set_background_from_template), dialog);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->background_template_combo = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Template:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_template_combo);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Custom image */
+ widget = gtk_file_chooser_button_new (
+ _("Selection a file"), GTK_FILE_CHOOSER_ACTION_OPEN);
+ g_signal_connect_swapped (
+ widget, "selection-changed",
+ G_CALLBACK (html_editor_page_dialog_set_background_image), dialog);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ dialog->priv->background_image_filechooser = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Custom:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_image_filechooser);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_page_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_PAGE_DIALOG,
+ "editor", editor,
+ "title", N_("Page Properties"),
+ NULL));
+}
diff --git a/e-util/e-html-editor-page-dialog.h b/e-util/e-html-editor-page-dialog.h
new file mode 100644
index 0000000000..5678efc4b6
--- /dev/null
+++ b/e-util/e-html-editor-page-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-page-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_PAGE_DIALOG_H
+#define E_HTML_EDITOR_PAGE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_PAGE_DIALOG \
+ (e_html_editor_page_dialog_get_type ())
+#define E_HTML_EDITOR_PAGE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialog))
+#define E_HTML_EDITOR_PAGE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogClass))
+#define E_IS_HTML_EDITOR_PAGE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG))
+#define E_IS_HTML_EDITOR_PAGE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_PAGE_DIALOG))
+#define E_HTML_EDITOR_PAGE_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorPageDialog EHTMLEditorPageDialog;
+typedef struct _EHTMLEditorPageDialogClass EHTMLEditorPageDialogClass;
+typedef struct _EHTMLEditorPageDialogPrivate EHTMLEditorPageDialogPrivate;
+
+struct _EHTMLEditorPageDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorPageDialogPrivate *priv;
+};
+
+struct _EHTMLEditorPageDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_page_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_page_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PAGE_DIALOG_H */
diff --git a/e-util/e-html-editor-paragraph-dialog.c b/e-util/e-html-editor-paragraph-dialog.c
new file mode 100644
index 0000000000..f0fce973a6
--- /dev/null
+++ b/e-util/e-html-editor-paragraph-dialog.c
@@ -0,0 +1,154 @@
+/*
+ * e-html-editor-paragraph-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-paragraph-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-action-combo-box.h"
+
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogPrivate))
+
+G_DEFINE_TYPE (
+ EHTMLEditorParagraphDialog,
+ e_html_editor_paragraph_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorParagraphDialogPrivate {
+ GtkWidget *style_combo;
+
+ GtkWidget *left_button;
+ GtkWidget *center_button;
+ GtkWidget *right_button;
+};
+
+static void
+html_editor_paragraph_dialog_constructed (GObject *object)
+{
+ GtkGrid *main_layout, *grid;
+ GtkWidget *widget;
+ EHTMLEditor *editor;
+ EHTMLEditorParagraphDialog *dialog;
+
+ dialog = E_HTML_EDITOR_PARAGRAPH_DIALOG (object);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == General == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Style */
+ widget = e_action_combo_box_new_with_action (
+ GTK_RADIO_ACTION (e_html_editor_get_action (editor, "style-normal")));
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ dialog->priv->style_combo = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Style:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->style_combo);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* == Alignment == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Alignment</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Left */
+ widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_LEFT);
+ gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+ gtk_activatable_set_related_action (
+ GTK_ACTIVATABLE (widget),
+ e_html_editor_get_action (editor, "justify-left"));
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+ dialog->priv->left_button = widget;
+
+ /* Center */
+ widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_CENTER);
+ gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ gtk_activatable_set_related_action (
+ GTK_ACTIVATABLE (widget),
+ e_html_editor_get_action (editor, "justify-center"));
+ dialog->priv->center_button = widget;
+
+ /* Right */
+ widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_RIGHT);
+ gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ gtk_activatable_set_related_action (
+ GTK_ACTIVATABLE (widget),
+ e_html_editor_get_action (editor, "justify-right"));
+ dialog->priv->right_button = widget;
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+static void
+e_html_editor_paragraph_dialog_class_init (EHTMLEditorParagraphDialogClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (
+ class, sizeof (EHTMLEditorParagraphDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = html_editor_paragraph_dialog_constructed;
+}
+
+static void
+e_html_editor_paragraph_dialog_init (EHTMLEditorParagraphDialog *dialog)
+{
+ dialog->priv = E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_PRIVATE (dialog);
+}
+
+GtkWidget *
+e_html_editor_paragraph_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG,
+ "editor", editor,
+ "title", N_("Paragraph Properties"),
+ NULL));
+}
diff --git a/e-util/e-html-editor-paragraph-dialog.h b/e-util/e-html-editor-paragraph-dialog.h
new file mode 100644
index 0000000000..17855bd3c8
--- /dev/null
+++ b/e-util/e-html-editor-paragraph-dialog.h
@@ -0,0 +1,71 @@
+/*
+ * e-html-editor-paragraph-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_PARAGRAPH_DIALOG_H
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG \
+ (e_html_editor_paragraph_dialog_get_type ())
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialog))
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogClass))
+#define E_IS_HTML_EDITOR_PARAGRAPH_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG))
+#define E_IS_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG))
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorParagraphDialog EHTMLEditorParagraphDialog;
+typedef struct _EHTMLEditorParagraphDialogClass EHTMLEditorParagraphDialogClass;
+typedef struct _EHTMLEditorParagraphDialogPrivate EHTMLEditorParagraphDialogPrivate;
+
+struct _EHTMLEditorParagraphDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorParagraphDialogPrivate *priv;
+};
+
+struct _EHTMLEditorParagraphDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_paragraph_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_paragraph_dialog_new
+ (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PARAGRAPH_DIALOG_H */
diff --git a/e-util/e-html-editor-private.h b/e-util/e-html-editor-private.h
new file mode 100644
index 0000000000..dc4658bd57
--- /dev/null
+++ b/e-util/e-html-editor-private.h
@@ -0,0 +1,103 @@
+/*
+ * e-html-editor-private.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_HTML_EDITOR_PRIVATE_H
+#define E_HTML_EDITOR_PRIVATE_H
+
+#include <e-action-combo-box.h>
+#include <e-color-combo.h>
+#include <e-html-editor.h>
+#include <e-html-editor-actions.h>
+#include <e-html-editor-cell-dialog.h>
+#include <e-html-editor-find-dialog.h>
+#include <e-html-editor-hrule-dialog.h>
+#include <e-html-editor-image-dialog.h>
+#include <e-html-editor-link-dialog.h>
+#include <e-html-editor-page-dialog.h>
+#include <e-html-editor-paragraph-dialog.h>
+#include <e-html-editor-replace-dialog.h>
+#include <e-html-editor-spell-check-dialog.h>
+#include <e-html-editor-table-dialog.h>
+#include <e-html-editor-text-dialog.h>
+#include <e-html-editor-view.h>
+
+#ifdef HAVE_XFREE
+#include <X11/XF86keysym.h>
+#endif
+
+#define ACTION(name) (E_HTML_EDITOR_ACTION_##name (editor))
+#define WIDGET(name) (E_HTML_EDITOR_WIDGETS_##name (editor))
+
+G_BEGIN_DECLS
+
+struct _EHTMLEditorPrivate {
+ GtkUIManager *manager;
+ GtkActionGroup *core_actions;
+ GtkActionGroup *html_actions;
+ GtkActionGroup *context_actions;
+ GtkActionGroup *html_context_actions;
+ GtkActionGroup *language_actions;
+ GtkActionGroup *spell_check_actions;
+ GtkActionGroup *suggestion_actions;
+
+ GtkWidget *main_menu;
+ GtkWidget *main_toolbar;
+ GtkWidget *edit_toolbar;
+ GtkWidget *html_toolbar;
+ GtkWidget *activity_bar;
+ GtkWidget *alert_bar;
+ GtkWidget *edit_area;
+
+ GtkWidget *find_dialog;
+ GtkWidget *replace_dialog;
+ GtkWidget *link_dialog;
+ GtkWidget *hrule_dialog;
+ GtkWidget *table_dialog;
+ GtkWidget *page_dialog;
+ GtkWidget *image_dialog;
+ GtkWidget *text_dialog;
+ GtkWidget *paragraph_dialog;
+ GtkWidget *cell_dialog;
+ GtkWidget *spell_check_dialog;
+
+ GtkWidget *color_combo_box;
+ GtkWidget *mode_combo_box;
+ GtkWidget *size_combo_box;
+ GtkWidget *style_combo_box;
+ GtkWidget *scrolled_window;
+
+ EHTMLEditorView *html_editor_view;
+ EHTMLEditorSelection *selection;
+
+ gchar *filename;
+
+ guint spell_suggestions_merge_id;
+
+ WebKitDOMNode *image;
+ WebKitDOMNode *table_cell;
+
+ gint editor_layout_row;
+};
+
+void editor_actions_init (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PRIVATE_H */
diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c
new file mode 100644
index 0000000000..7addcdfdad
--- /dev/null
+++ b/e-util/e-html-editor-replace-dialog.c
@@ -0,0 +1,288 @@
+/*
+ * e-html-editor-replace-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-replace-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogPrivate))
+
+G_DEFINE_TYPE (
+ EHTMLEditorReplaceDialog,
+ e_html_editor_replace_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorReplaceDialogPrivate {
+ GtkWidget *search_entry;
+ GtkWidget *replace_entry;
+
+ GtkWidget *case_sensitive;
+ GtkWidget *backwards;
+ GtkWidget *wrap;
+
+ GtkWidget *result_label;
+
+ GtkWidget *skip_button;
+ GtkWidget *replace_button;
+ GtkWidget *replace_all_button;
+
+ EHTMLEditor *editor;
+};
+
+enum {
+ PROP_0,
+ PROP_EDITOR
+};
+
+static gboolean
+jump (EHTMLEditorReplaceDialog *dialog)
+{
+ EHTMLEditor *editor;
+ WebKitWebView *webview;
+ gboolean found;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ webview = WEBKIT_WEB_VIEW (
+ e_html_editor_get_view (editor));
+
+ found = webkit_web_view_search_text (
+ webview,
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)),
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)),
+ !gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->backwards)),
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->wrap)));
+
+ return found;
+}
+
+static void
+html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog)
+{
+ if (!jump (dialog)) {
+ gtk_label_set_label (
+ GTK_LABEL (dialog->priv->result_label),
+ N_("No match found"));
+ gtk_widget_show (dialog->priv->result_label);
+ } else {
+ gtk_widget_hide (dialog->priv->result_label);
+ }
+}
+
+static void
+html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ /* Jump to next matching word */
+ if (!jump (dialog)) {
+ gtk_label_set_label (
+ GTK_LABEL (dialog->priv->result_label),
+ N_("No match found"));
+ gtk_widget_show (dialog->priv->result_label);
+ return;
+ } else {
+ gtk_widget_hide (dialog->priv->result_label);
+ }
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_replace (
+ selection,
+ gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)));
+}
+
+static void
+html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
+{
+ gint i = 0;
+ gchar *result;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ const gchar *replacement;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ replacement = gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry));
+
+ while (jump (dialog)) {
+ e_html_editor_selection_replace (selection, replacement);
+ i++;
+
+ /* Jump behind the word */
+ e_html_editor_selection_move (
+ selection, TRUE, E_HTML_EDITOR_SELECTION_GRANULARITY_WORD);
+ }
+
+ result = g_strdup_printf (_("%d occurences replaced"), i);
+ gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result);
+ gtk_widget_show (dialog->priv->result_label);
+ g_free (result);
+}
+
+static void
+html_editor_replace_dialog_entry_changed (EHTMLEditorReplaceDialog *dialog)
+{
+ gboolean ready;
+ ready = ((gtk_entry_get_text_length (
+ GTK_ENTRY (dialog->priv->search_entry)) != 0) &&
+ (gtk_entry_get_text_length (
+ GTK_ENTRY (dialog->priv->replace_entry)) != 0));
+
+ gtk_widget_set_sensitive (dialog->priv->skip_button, ready);
+ gtk_widget_set_sensitive (dialog->priv->replace_button, ready);
+ gtk_widget_set_sensitive (dialog->priv->replace_all_button, ready);
+}
+
+static void
+html_editor_replace_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
+
+ gtk_widget_grab_focus (dialog->priv->search_entry);
+ gtk_widget_hide (dialog->priv->result_label);
+
+ /* Chain up to parent implementation */
+ GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_replace_dialog_class_init (EHTMLEditorReplaceDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorReplaceDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_replace_dialog_show;
+}
+
+static void
+e_html_editor_replace_dialog_init (EHTMLEditorReplaceDialog *dialog)
+{
+ GtkGrid *main_layout;
+ GtkWidget *widget, *layout;
+ GtkBox *button_box;
+
+ dialog->priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ widget = gtk_entry_new ();
+ gtk_grid_attach (main_layout, widget, 1, 0, 2, 1);
+ dialog->priv->search_entry = widget;
+ g_signal_connect_swapped (
+ widget, "notify::text-length",
+ G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("R_eplace:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->search_entry);
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ widget = gtk_entry_new ();
+ gtk_grid_attach (main_layout, widget, 1, 1, 2, 1);
+ dialog->priv->replace_entry = widget;
+ g_signal_connect_swapped (
+ widget, "notify::text-length",
+ G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog);
+
+ widget = gtk_label_new_with_mnemonic (_("_With:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->replace_entry);
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+ layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_grid_attach (main_layout, layout, 1, 2, 2, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (_("Search _backwards"));
+ gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+ dialog->priv->backwards = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Case sensitive"));
+ gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+ dialog->priv->case_sensitive = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (_("Wra_p search"));
+ gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+ dialog->priv->wrap = widget;
+
+ widget = gtk_label_new ("");
+ gtk_grid_attach (main_layout, widget, 0, 3, 2, 1);
+ dialog->priv->result_label = widget;
+
+ button_box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+
+ widget = gtk_button_new_with_mnemonic (_("_Skip"));
+ gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+ gtk_widget_set_sensitive (widget, FALSE);
+ dialog->priv->skip_button = widget;
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_replace_dialog_skip_cb), dialog);
+
+ widget = gtk_button_new_with_mnemonic (_("_Replace"));
+ gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+ gtk_widget_set_sensitive (widget, FALSE);
+ dialog->priv->replace_button = widget;
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_replace_dialog_replace_cb), dialog);
+
+ widget = gtk_button_new_with_mnemonic (_("Replace _All"));
+ gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+ gtk_widget_set_sensitive (widget, FALSE);
+ dialog->priv->replace_all_button = widget;
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_replace_dialog_replace_all_cb), dialog);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_replace_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_REPLACE_DIALOG,
+ "editor", editor,
+ "icon-name", GTK_STOCK_FIND_AND_REPLACE,
+ "resizable", FALSE,
+ "title", N_("Replace"),
+ "transient-for", gtk_widget_get_toplevel (GTK_WIDGET (editor)),
+ "type", GTK_WINDOW_TOPLEVEL,
+ "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+ NULL));
+}
diff --git a/e-util/e-html-editor-replace-dialog.h b/e-util/e-html-editor-replace-dialog.h
new file mode 100644
index 0000000000..f253e428aa
--- /dev/null
+++ b/e-util/e-html-editor-replace-dialog.h
@@ -0,0 +1,71 @@
+/*
+ * e-html-editor-replace-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_REPLACE_DIALOG_H
+#define E_HTML_EDITOR_REPLACE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_REPLACE_DIALOG \
+ (e_html_editor_replace_dialog_get_type ())
+#define E_HTML_EDITOR_REPLACE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialog))
+#define E_HTML_EDITOR_REPLACE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogClass))
+#define E_IS_HTML_EDITOR_REPLACE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG))
+#define E_IS_HTML_EDITOR_REPLACE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_REPLACE_DIALOG))
+#define E_HTML_EDITOR_REPLACE_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorReplaceDialog EHTMLEditorReplaceDialog;
+typedef struct _EHTMLEditorReplaceDialogClass EHTMLEditorReplaceDialogClass;
+typedef struct _EHTMLEditorReplaceDialogPrivate EHTMLEditorReplaceDialogPrivate;
+
+struct _EHTMLEditorReplaceDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorReplaceDialogPrivate *priv;
+};
+
+struct _EHTMLEditorReplaceDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_replace_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_replace_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_REPLACE_DIALOG_H */
+
diff --git a/e-util/e-html-editor-selection.c b/e-util/e-html-editor-selection.c
new file mode 100644
index 0000000000..c109063f41
--- /dev/null
+++ b/e-util/e-html-editor-selection.c
@@ -0,0 +1,5576 @@
+/*
+ * e-html-editor-selection.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-selection.h"
+#include "e-html-editor-view.h"
+#include "e-html-editor.h"
+#include "e-html-editor-utils.h"
+
+#include <e-util/e-util.h>
+
+#include <webkit/webkit.h>
+#include <webkit/webkitdom.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define E_HTML_EDITOR_SELECTION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionPrivate))
+
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+#define UNICODE_NBSP "\xc2\xa0"
+
+#define SPACES_PER_INDENTATION 4
+#define SPACES_PER_LIST_LEVEL 8
+
+/**
+ * EHTMLEditorSelection
+ *
+ * The #EHTMLEditorSelection object represents current position of the cursor
+ * with the editor or current text selection within the editor. To obtain
+ * valid #EHTMLEditorSelection, call e_html_editor_view_get_selection().
+ */
+
+struct _EHTMLEditorSelectionPrivate {
+
+ GWeakRef html_editor_view;
+ gulong selection_changed_handler_id;
+
+ gchar *text;
+
+ gboolean is_bold;
+ gboolean is_italic;
+ gboolean is_underline;
+ gboolean is_monospaced;
+ gboolean is_strikethrough;
+
+ gchar *background_color;
+ gchar *font_color;
+ gchar *font_family;
+
+ gulong selection_offset;
+
+ gint word_wrap_length;
+ guint font_size;
+
+ EHTMLEditorSelectionAlignment alignment;
+};
+
+enum {
+ PROP_0,
+ PROP_ALIGNMENT,
+ PROP_BACKGROUND_COLOR,
+ PROP_BLOCK_FORMAT,
+ PROP_BOLD,
+ PROP_HTML_EDITOR_VIEW,
+ PROP_FONT_COLOR,
+ PROP_FONT_NAME,
+ PROP_FONT_SIZE,
+ PROP_INDENTED,
+ PROP_ITALIC,
+ PROP_MONOSPACED,
+ PROP_STRIKETHROUGH,
+ PROP_SUBSCRIPT,
+ PROP_SUPERSCRIPT,
+ PROP_TEXT,
+ PROP_UNDERLINE
+};
+
+static const GdkRGBA black = { 0 };
+
+G_DEFINE_TYPE (
+ EHTMLEditorSelection,
+ e_html_editor_selection,
+ G_TYPE_OBJECT
+);
+
+static WebKitDOMRange *
+html_editor_selection_get_current_range (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range = NULL;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ document = webkit_web_view_get_dom_document (web_view);
+ window = webkit_dom_document_get_default_view (document);
+ if (!window)
+ goto exit;
+
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+ if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection))
+ goto exit;
+
+ if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
+ goto exit;
+
+ range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+ exit:
+ g_object_unref (view);
+
+ return range;
+}
+
+static gboolean
+get_has_style (EHTMLEditorSelection *selection,
+ const gchar *style_tag)
+{
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+ gboolean result;
+ gint tag_len;
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_start_container (range, NULL);
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ tag_len = strlen (style_tag);
+ result = FALSE;
+ while (!result && element) {
+ gchar *element_tag;
+ gboolean accept_citation = FALSE;
+
+ element_tag = webkit_dom_element_get_tag_name (element);
+
+ if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) {
+ accept_citation = TRUE;
+ result = ((strlen (element_tag) == 10 /* strlen ("blockquote") */) &&
+ (g_ascii_strncasecmp (element_tag, "blockquote", 10) == 0));
+ if (element_has_class (element, "-x-evo-indented"))
+ result = FALSE;
+ } else {
+ result = ((tag_len == strlen (element_tag)) &&
+ (g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0));
+ }
+
+ /* Special case: <blockquote type=cite> marks quotation, while
+ * just <blockquote> is used for indentation. If the <blockquote>
+ * has type=cite, then ignore it unless style_tag is "citation" */
+ if (result && g_ascii_strncasecmp (element_tag, "blockquote", 10) == 0) {
+ if (webkit_dom_element_has_attribute (element, "type")) {
+ gchar *type;
+ type = webkit_dom_element_get_attribute (element, "type");
+ if (!accept_citation && (g_ascii_strncasecmp (type, "cite", 4) == 0)) {
+ result = FALSE;
+ }
+ g_free (type);
+ } else {
+ if (accept_citation)
+ result = FALSE;
+ }
+ }
+
+ g_free (element_tag);
+
+ if (result)
+ break;
+
+ element = webkit_dom_node_get_parent_element (
+ WEBKIT_DOM_NODE (element));
+ }
+
+ return result;
+}
+
+static gchar *
+get_font_property (EHTMLEditorSelection *selection,
+ const gchar *font_property)
+{
+ WebKitDOMRange *range;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ gchar *value;
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return NULL;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ element = e_html_editor_dom_node_find_parent_element (node, "FONT");
+ if (!element)
+ return NULL;
+
+ g_object_get (G_OBJECT (element), font_property, &value, NULL);
+
+ return value;
+}
+
+static void
+html_editor_selection_selection_changed_cb (WebKitWebView *webview,
+ EHTMLEditorSelection *selection)
+{
+ g_object_freeze_notify (G_OBJECT (selection));
+
+ g_object_notify (G_OBJECT (selection), "alignment");
+ g_object_notify (G_OBJECT (selection), "background-color");
+ g_object_notify (G_OBJECT (selection), "bold");
+ g_object_notify (G_OBJECT (selection), "font-name");
+ g_object_notify (G_OBJECT (selection), "font-size");
+ g_object_notify (G_OBJECT (selection), "font-color");
+ g_object_notify (G_OBJECT (selection), "block-format");
+ g_object_notify (G_OBJECT (selection), "indented");
+ g_object_notify (G_OBJECT (selection), "italic");
+ g_object_notify (G_OBJECT (selection), "monospaced");
+ g_object_notify (G_OBJECT (selection), "strikethrough");
+ g_object_notify (G_OBJECT (selection), "subscript");
+ g_object_notify (G_OBJECT (selection), "superscript");
+ g_object_notify (G_OBJECT (selection), "text");
+ g_object_notify (G_OBJECT (selection), "underline");
+
+ g_object_thaw_notify (G_OBJECT (selection));
+}
+
+void
+e_html_editor_selection_block_selection_changed (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_signal_handlers_block_by_func (
+ view, html_editor_selection_selection_changed_cb, selection);
+ g_object_unref (view);
+}
+
+void
+e_html_editor_selection_unblock_selection_changed (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_signal_handlers_unblock_by_func (
+ view, html_editor_selection_selection_changed_cb, selection);
+ g_object_unref (view);
+}
+
+static void
+html_editor_selection_set_html_editor_view (EHTMLEditorSelection *selection,
+ EHTMLEditorView *view)
+{
+ gulong handler_id;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ g_weak_ref_set (&selection->priv->html_editor_view, view);
+
+ handler_id = g_signal_connect (
+ view, "selection-changed",
+ G_CALLBACK (html_editor_selection_selection_changed_cb),
+ selection);
+
+ selection->priv->selection_changed_handler_id = handler_id;
+}
+
+static void
+html_editor_selection_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdkRGBA rgba = { 0 };
+
+ switch (property_id) {
+ case PROP_ALIGNMENT:
+ g_value_set_int (
+ value,
+ e_html_editor_selection_get_alignment (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_BACKGROUND_COLOR:
+ g_value_set_string (
+ value,
+ e_html_editor_selection_get_background_color (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_BLOCK_FORMAT:
+ g_value_set_int (
+ value,
+ e_html_editor_selection_get_block_format (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_BOLD:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_bold (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_HTML_EDITOR_VIEW:
+ g_value_take_object (
+ value,
+ e_html_editor_selection_ref_html_editor_view (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_FONT_COLOR:
+ e_html_editor_selection_get_font_color (
+ E_HTML_EDITOR_SELECTION (object), &rgba);
+ g_value_set_boxed (value, &rgba);
+ return;
+
+ case PROP_FONT_NAME:
+ g_value_set_string (
+ value,
+ e_html_editor_selection_get_font_name (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_FONT_SIZE:
+ g_value_set_int (
+ value,
+ e_html_editor_selection_get_font_size (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_INDENTED:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_indented (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_ITALIC:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_italic (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_MONOSPACED:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_monospaced (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_STRIKETHROUGH:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_strikethrough (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_SUBSCRIPT:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_subscript (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_SUPERSCRIPT:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_superscript (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+
+ case PROP_TEXT:
+ g_value_set_string (
+ value,
+ e_html_editor_selection_get_string (
+ E_HTML_EDITOR_SELECTION (object)));
+ break;
+
+ case PROP_UNDERLINE:
+ g_value_set_boolean (
+ value,
+ e_html_editor_selection_is_underline (
+ E_HTML_EDITOR_SELECTION (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_selection_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ALIGNMENT:
+ e_html_editor_selection_set_alignment (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_int (value));
+ return;
+
+ case PROP_BACKGROUND_COLOR:
+ e_html_editor_selection_set_background_color (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_BOLD:
+ e_html_editor_selection_set_bold (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_HTML_EDITOR_VIEW:
+ html_editor_selection_set_html_editor_view (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_FONT_COLOR:
+ e_html_editor_selection_set_font_color (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_BLOCK_FORMAT:
+ e_html_editor_selection_set_block_format (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_int (value));
+ return;
+
+ case PROP_FONT_NAME:
+ e_html_editor_selection_set_font_name (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_FONT_SIZE:
+ e_html_editor_selection_set_font_size (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_int (value));
+ return;
+
+ case PROP_ITALIC:
+ e_html_editor_selection_set_italic (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MONOSPACED:
+ e_html_editor_selection_set_monospaced (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_STRIKETHROUGH:
+ e_html_editor_selection_set_strikethrough (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SUBSCRIPT:
+ e_html_editor_selection_set_subscript (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SUPERSCRIPT:
+ e_html_editor_selection_set_superscript (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_UNDERLINE:
+ e_html_editor_selection_set_underline (
+ E_HTML_EDITOR_SELECTION (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_selection_dispose (GObject *object)
+{
+ EHTMLEditorSelectionPrivate *priv;
+ EHTMLEditorView *view;
+
+ priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (object);
+
+ view = g_weak_ref_get (&priv->html_editor_view);
+ if (view != NULL) {
+ g_signal_handler_disconnect (
+ view, priv->selection_changed_handler_id);
+ priv->selection_changed_handler_id = 0;
+ g_object_unref (view);
+ }
+
+ g_weak_ref_set (&priv->html_editor_view, NULL);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_html_editor_selection_parent_class)->dispose (object);
+}
+
+static void
+html_editor_selection_finalize (GObject *object)
+{
+ EHTMLEditorSelection *selection = E_HTML_EDITOR_SELECTION (object);
+
+ g_free (selection->priv->text);
+ g_free (selection->priv->background_color);
+ g_free (selection->priv->font_color);
+ g_free (selection->priv->font_family);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_html_editor_selection_parent_class)->finalize (object);
+}
+
+static void
+e_html_editor_selection_class_init (EHTMLEditorSelectionClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorSelectionPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = html_editor_selection_get_property;
+ object_class->set_property = html_editor_selection_set_property;
+ object_class->dispose = html_editor_selection_dispose;
+ object_class->finalize = html_editor_selection_finalize;
+
+ /**
+ * EHTMLEditorSelectionalignment
+ *
+ * Holds alignment of current paragraph.
+ */
+ /* FIXME: Convert the enum to a proper type */
+ g_object_class_install_property (
+ object_class,
+ PROP_ALIGNMENT,
+ g_param_spec_int (
+ "alignment",
+ NULL,
+ NULL,
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT,
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+ G_PARAM_READWRITE));
+
+ /**
+ * EHTMLEditorSelectionbackground-color
+ *
+ * Holds background color of current selection or at current cursor
+ * position.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_BACKGROUND_COLOR,
+ g_param_spec_string (
+ "background-color",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * EHTMLEditorSelectionblock-format
+ *
+ * Holds block format of current paragraph. See
+ * #EHTMLEditorSelectionBlockFormat for valid values.
+ */
+ /* FIXME Convert the EHTMLEditorSelectionBlockFormat
+ * enum to a proper type. */
+ g_object_class_install_property (
+ object_class,
+ PROP_BLOCK_FORMAT,
+ g_param_spec_int (
+ "block-format",
+ NULL,
+ NULL,
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionbold
+ *
+ * Holds whether current selection or text at current cursor position
+ * is bold.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_BOLD,
+ g_param_spec_boolean (
+ "bold",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HTML_EDITOR_VIEW,
+ g_param_spec_object (
+ "html-editor-view",
+ NULL,
+ NULL,
+ E_TYPE_HTML_EDITOR_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionfont-color
+ *
+ * Holds font color of current selection or at current cursor position.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_FONT_COLOR,
+ g_param_spec_boxed (
+ "font-color",
+ NULL,
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionfont-name
+ *
+ * Holds name of font in current selection or at current cursor
+ * position.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_FONT_NAME,
+ g_param_spec_string (
+ "font-name",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionfont-size
+ *
+ * Holds point size of current selection or at current cursor position.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_FONT_SIZE,
+ g_param_spec_int (
+ "font-size",
+ NULL,
+ NULL,
+ 1,
+ 7,
+ 3,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionindented
+ *
+ * Holds whether current paragraph is indented. This does not include
+ * citations.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_INDENTED,
+ g_param_spec_boolean (
+ "indented",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionitalic
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is italic.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_ITALIC,
+ g_param_spec_boolean (
+ "italic",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionmonospaced
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is monospaced.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_MONOSPACED,
+ g_param_spec_boolean (
+ "monospaced",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionstrikethrough
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is strikethrough.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_STRIKETHROUGH,
+ g_param_spec_boolean (
+ "strikethrough",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionsuperscript
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is in superscript.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_SUPERSCRIPT,
+ g_param_spec_boolean (
+ "superscript",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionsubscript
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is in subscript.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_SUBSCRIPT,
+ g_param_spec_boolean (
+ "subscript",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectiontext
+ *
+ * Holds always up-to-date text of current selection.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_TEXT,
+ g_param_spec_string (
+ "text",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorSelectionunderline
+ *
+ * Holds whether current selection or letter at current cursor position
+ * is underlined.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_UNDERLINE,
+ g_param_spec_boolean (
+ "underline",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_html_editor_selection_init (EHTMLEditorSelection *selection)
+{
+ GSettings *g_settings;
+
+ selection->priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (selection);
+
+ g_settings = g_settings_new ("org.gnome.evolution.mail");
+ selection->priv->word_wrap_length =
+ g_settings_get_int (g_settings, "composer-word-wrap-length");
+ g_object_unref (g_settings);
+}
+
+gint
+e_html_editor_selection_get_word_wrap_length (EHTMLEditorSelection *selection)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), 72);
+
+ return selection->priv->word_wrap_length;
+}
+
+/**
+ * e_html_editor_selection_ref_html_editor_view:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns a new reference to @selection's #EHTMLEditorView. Unreference
+ * the #EHTMLEditorView with g_object_unref() when finished with it.
+ *
+ * Returns: an #EHTMLEditorView
+ **/
+EHTMLEditorView *
+e_html_editor_selection_ref_html_editor_view (EHTMLEditorSelection *selection)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ return g_weak_ref_get (&selection->priv->html_editor_view);
+}
+
+/**
+ * e_html_editor_selection_has_text:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection contains any text.
+ *
+ * Returns: @TRUE when current selection contains text, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_has_text (EHTMLEditorSelection *selection)
+{
+ WebKitDOMRange *range;
+ WebKitDOMNode *node;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ range = html_editor_selection_get_current_range (selection);
+
+ node = webkit_dom_range_get_start_container (range, NULL);
+ if (webkit_dom_node_get_node_type (node) == 3)
+ return TRUE;
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (webkit_dom_node_get_node_type (node) == 3)
+ return TRUE;
+
+ node = WEBKIT_DOM_NODE (webkit_dom_range_clone_contents (range, NULL));
+ while (node) {
+ if (webkit_dom_node_get_node_type (node) == 3)
+ return TRUE;
+
+ if (webkit_dom_node_has_child_nodes (node)) {
+ node = webkit_dom_node_get_first_child (node);
+ } else if (webkit_dom_node_get_next_sibling (node)) {
+ node = webkit_dom_node_get_next_sibling (node);
+ } else {
+ node = webkit_dom_node_get_parent_node (node);
+ if (node) {
+ node = webkit_dom_node_get_next_sibling (node);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * e_html_editor_selection_get_caret_word:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns word under cursor.
+ *
+ * Returns: A newly allocated string with current caret word or @NULL when there
+ * is no text under cursor or when selection is active. [transfer-full].
+ */
+gchar *
+e_html_editor_selection_get_caret_word (EHTMLEditorSelection *selection)
+{
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ range = html_editor_selection_get_current_range (selection);
+
+ /* Don't operate on the visible selection */
+ range = webkit_dom_range_clone_range (range, NULL);
+ webkit_dom_range_expand (range, "word", NULL);
+
+ return webkit_dom_range_to_string (range, NULL);
+}
+
+/**
+ * e_html_editor_selection_replace_caret_word:
+ * @selection: an #EHTMLEditorSelection
+ * @replacement: a string to replace current caret word with
+ *
+ * Replaces current word under cursor with @replacement.
+ */
+void
+e_html_editor_selection_replace_caret_word (EHTMLEditorSelection *selection,
+ const gchar *replacement)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (replacement != NULL);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ range = html_editor_selection_get_current_range (selection);
+ document = webkit_web_view_get_dom_document (web_view);
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+
+ webkit_dom_range_expand (range, "word", NULL);
+ webkit_dom_dom_selection_add_range (dom_selection, range);
+
+ e_html_editor_selection_insert_html (selection, replacement);
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_get_string:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns currently selected string.
+ *
+ * Returns: A pointer to content of current selection. The string is owned by
+ * #EHTMLEditorSelection and should not be free'd.
+ */
+const gchar *
+e_html_editor_selection_get_string (EHTMLEditorSelection *selection)
+{
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return NULL;
+
+ g_free (selection->priv->text);
+ selection->priv->text = webkit_dom_range_get_text (range);
+
+ return selection->priv->text;
+}
+
+/**
+ * e_html_editor_selection_replace:
+ * @selection: an #EHTMLEditorSelection
+ * @new_string: a string to replace current selection with
+ *
+ * Replaces currently selected text with @new_string.
+ */
+void
+e_html_editor_selection_replace (EHTMLEditorSelection *selection,
+ const gchar *new_string)
+{
+ EHTMLEditorView *view;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, new_string);
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_get_list_alignment_from_node:
+ * @node: #an WebKitDOMNode
+ *
+ * Returns alignment of given list.
+ *
+ * Returns: #EHTMLEditorSelectionAlignment
+ */
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_list_alignment_from_node (WebKitDOMNode *node)
+{
+ if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-left"))
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+ if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-center"))
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
+ if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-right"))
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
+
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+}
+
+/**
+ * e_html_editor_selection_get_alignment:
+ * @selection: #an EHTMLEditorSelection
+ *
+ * Returns alignment of current paragraph
+ *
+ * Returns: #EHTMLEditorSelectionAlignment
+ */
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_alignment (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorSelectionAlignment alignment;
+ EHTMLEditorView *view;
+ gchar *value;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMElement *element;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (
+ E_IS_HTML_EDITOR_SELECTION (selection),
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+
+ node = webkit_dom_range_get_start_container (range, NULL);
+ if (!node)
+ return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
+
+ if (!value || !*value ||
+ (g_ascii_strncasecmp (value, "left", 4) == 0)) {
+ alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+ } else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
+ alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
+ } else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
+ alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
+ } else {
+ alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+ }
+
+ g_free (value);
+
+ return alignment;
+}
+
+static void
+set_ordered_list_type_to_element (WebKitDOMElement *list,
+ EHTMLEditorSelectionBlockFormat format)
+{
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST)
+ webkit_dom_element_remove_attribute (list, "type");
+ else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+ webkit_dom_element_set_attribute (list, "type", "A", NULL);
+ else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN)
+ webkit_dom_element_set_attribute (list, "type", "I", NULL);
+}
+
+static WebKitDOMElement *
+create_list_element (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ EHTMLEditorSelectionBlockFormat format,
+ gint level,
+ gboolean html_mode)
+{
+ WebKitDOMElement *list;
+ gint offset = -SPACES_PER_LIST_LEVEL;
+ gboolean inserting_unordered_list =
+ format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+ list = webkit_dom_document_create_element (
+ document, inserting_unordered_list ? "UL" : "OL", NULL);
+
+ set_ordered_list_type_to_element (list, format);
+
+ if (level >= 0)
+ offset = (level + 1) * -SPACES_PER_LIST_LEVEL;
+
+ if (!html_mode)
+ e_html_editor_selection_set_paragraph_style (
+ selection, list, -1, offset, "");
+
+ return list;
+}
+
+static void
+remove_node (WebKitDOMNode *node)
+{
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node), node, NULL);
+}
+
+static void
+format_change_list_from_list (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ EHTMLEditorSelectionBlockFormat to,
+ gboolean html_mode)
+{
+ gboolean after_selection_end = FALSE;
+ WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list;
+ WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+
+ if (!selection_start_marker || !selection_end_marker)
+ return;
+
+ new_list = create_list_element (selection, document, to, 0, html_mode);
+
+ /* Copy elements from previous block to list */
+ item = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+ source_list = webkit_dom_node_get_parent_node (item);
+ current_list = source_list;
+ source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+ element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+ while (item) {
+ WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+ webkit_dom_node_append_child (
+ after_selection_end ?
+ source_list_clone : WEBKIT_DOM_NODE (new_list),
+ WEBKIT_DOM_NODE (item),
+ NULL);
+ }
+
+ if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker))) {
+ source_list_clone = webkit_dom_node_clone_node (current_list, FALSE);
+ after_selection_end = TRUE;
+ }
+
+ if (!next_item) {
+ if (after_selection_end)
+ break;
+ current_list = webkit_dom_node_get_next_sibling (current_list);
+ next_item = webkit_dom_node_get_first_child (current_list);
+ }
+ item = next_item;
+ }
+
+ if (webkit_dom_node_has_child_nodes (source_list_clone))
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ WEBKIT_DOM_NODE (source_list_clone),
+ webkit_dom_node_get_next_sibling (source_list), NULL);
+ if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list)))
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ WEBKIT_DOM_NODE (new_list),
+ webkit_dom_node_get_next_sibling (source_list), NULL);
+ if (!webkit_dom_node_has_child_nodes (source_list))
+ remove_node (source_list);
+}
+
+/**
+ * e_html_editor_selection_set_alignment:
+ * @selection: an #EHTMLEditorSelection
+ * @alignment: an #EHTMLEditorSelectionAlignment value to apply
+ *
+ * Sets alignment of current paragraph to give @alignment.
+ */
+void
+e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionAlignment alignment)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+ const gchar *class = "";
+ WebKitDOMDocument *document;
+ WebKitDOMElement *selection_start_marker;
+ WebKitDOMNode *parent;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_get_alignment (selection) == alignment)
+ return;
+
+ switch (alignment) {
+ case E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER:
+ command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER;
+ class = "-x-evo-list-item-align-center";
+ break;
+
+ case E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT:
+ command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT;
+ break;
+
+ case E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT:
+ command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT;
+ class = "-x-evo-list-item-align-right";
+ break;
+ }
+
+ selection->priv->alignment = alignment;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ e_html_editor_selection_save (selection);
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+
+ if (!selection_start_marker) {
+ g_object_unref (view);
+ return;
+ }
+
+ parent = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent)) {
+ element_remove_class (
+ WEBKIT_DOM_ELEMENT (parent),
+ "-x-evo-list-item-align-center");
+ element_remove_class (
+ WEBKIT_DOM_ELEMENT (parent),
+ "-x-evo-list-item-align-right");
+
+ element_add_class (WEBKIT_DOM_ELEMENT (parent), class);
+ } else {
+ e_html_editor_view_exec_command (view, command, NULL);
+ }
+
+ e_html_editor_selection_restore (selection);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "alignment");
+}
+
+/**
+ * e_html_editor_selection_get_background_color:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns background color of currently selected text or letter at current
+ * cursor position.
+ *
+ * Returns: A string with code of current background color.
+ */
+const gchar *
+e_html_editor_selection_get_background_color (EHTMLEditorSelection *selection)
+{
+ WebKitDOMNode *ancestor;
+ WebKitDOMRange *range;
+ WebKitDOMCSSStyleDeclaration *css;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ range = html_editor_selection_get_current_range (selection);
+
+ ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor));
+ selection->priv->background_color =
+ webkit_dom_css_style_declaration_get_property_value (
+ css, "background-color");
+
+ return selection->priv->background_color;
+}
+
+/**
+ * e_html_editor_selection_set_background_color:
+ * @selection: an #EHTMLEditorSelection
+ * @color: code of new background color to set
+ *
+ * Changes background color of current selection or letter at current cursor
+ * position to @color.
+ */
+void
+e_html_editor_selection_set_background_color (EHTMLEditorSelection *selection,
+ const gchar *color)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (color != NULL && *color != '\0');
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR;
+ e_html_editor_view_exec_command (view, command, color);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "background-color");
+}
+
+static gint
+get_indentation_level (WebKitDOMElement *element)
+{
+ WebKitDOMElement *parent;
+ gint level = 0;
+
+ if (element_has_class (element, "-x-evo-indented"))
+ level++;
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+ /* Count level of indentation */
+ while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ if (element_has_class (parent, "-x-evo-indented"))
+ level++;
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
+ }
+
+ return level;
+}
+
+static WebKitDOMNode *
+get_block_node (WebKitDOMRange *range)
+{
+ WebKitDOMNode *node;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ if (!WEBKIT_DOM_IS_ELEMENT (node))
+ node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-temp-text-wrapper"))
+ node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+ return node;
+}
+
+/**
+ * e_html_editor_selection_get_list_format_from_node:
+ * @node: an #WebKitDOMNode
+ *
+ * Returns block format of given list.
+ *
+ * Returns: #EHTMLEditorSelectionBlockFormat
+ */
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_list_format_from_node (WebKitDOMNode *node)
+{
+ EHTMLEditorSelectionBlockFormat format =
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
+ return -1;
+
+ if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node))
+ return format;
+
+ if (WEBKIT_DOM_IS_ELEMENT (node)) {
+ gchar *type_value = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "type");
+
+ if (!type_value)
+ return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+
+ if (!*type_value)
+ format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+ else if (g_ascii_strcasecmp (type_value, "A") == 0)
+ format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+ else if (g_ascii_strcasecmp (type_value, "I") == 0)
+ format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+ g_free (type_value);
+ }
+
+ return -1;
+}
+
+/**
+ * e_html_editor_selection_get_block_format:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns block format of current paragraph.
+ *
+ * Returns: #EHTMLEditorSelectionBlockFormat
+ */
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_block_format (EHTMLEditorSelection *selection)
+{
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+ WebKitDOMElement *element;
+ EHTMLEditorSelectionBlockFormat result;
+
+ g_return_val_if_fail (
+ E_IS_HTML_EDITOR_SELECTION (selection),
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+
+ node = webkit_dom_range_get_start_container (range, NULL);
+
+ if (e_html_editor_dom_node_find_parent_element (node, "UL")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+ } else if ((element = e_html_editor_dom_node_find_parent_element (node, "OL")) != NULL) {
+ if (webkit_dom_element_has_attribute (element, "type")) {
+ gchar *type;
+
+ type = webkit_dom_element_get_attribute (element, "type");
+ if (type && ((*type == 'a') || (*type == 'A'))) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+ } else if (type && ((*type == 'i') || (*type == 'I'))) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+ } else {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+ }
+
+ g_free (type);
+ } else {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+ }
+ } else if (e_html_editor_dom_node_find_parent_element (node, "PRE")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "ADDRESS")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H1")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H2")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H3")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H4")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H5")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5;
+ } else if (e_html_editor_dom_node_find_parent_element (node, "H6")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6;
+ } else if ((element = e_html_editor_dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) {
+ if (element_has_class (element, "-x-evo-indented"))
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+ else {
+ WebKitDOMNode *block = get_block_node (range);
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph"))
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+ else
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE;
+ }
+ } else if (e_html_editor_dom_node_find_parent_element (node, "P")) {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+ } else {
+ result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+ }
+
+ return result;
+}
+
+static gboolean
+is_caret_position_node (WebKitDOMNode *node)
+{
+ return element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position");
+}
+
+static void
+merge_list_into_list (WebKitDOMNode *from,
+ WebKitDOMNode *to,
+ gboolean insert_before)
+{
+ WebKitDOMNode *item;
+
+ if (!(to && from))
+ return;
+
+ while ((item = webkit_dom_node_get_first_child (from)) != NULL) {
+ if (insert_before)
+ webkit_dom_node_insert_before (
+ to, item, webkit_dom_node_get_last_child (to), NULL);
+ else
+ webkit_dom_node_append_child (to, item, NULL);
+ }
+
+ if (!webkit_dom_node_has_child_nodes (from))
+ remove_node (from);
+
+}
+
+static void
+merge_lists_if_possible (WebKitDOMNode *list)
+{
+ EHTMLEditorSelectionBlockFormat format, prev, next;
+ WebKitDOMNode *prev_sibling, *next_sibling;
+
+ prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list));
+ next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list));
+
+ format = e_html_editor_selection_get_list_format_from_node (list),
+ prev = e_html_editor_selection_get_list_format_from_node (prev_sibling);
+ next = e_html_editor_selection_get_list_format_from_node (next_sibling);
+
+ if (format == prev && format != -1 && prev != -1)
+ merge_list_into_list (prev_sibling, list, TRUE);
+
+ if (format == next && format != -1 && next != -1)
+ merge_list_into_list (next_sibling, list, FALSE);
+}
+
+static void
+remove_wrapping (WebKitDOMElement *element)
+{
+ WebKitDOMNodeList *list;
+ gint ii, length;
+
+ list = webkit_dom_element_query_selector_all (
+ element, "br.-x-evo-wrap-br", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++)
+ remove_node (webkit_dom_node_list_item (list, ii));
+}
+
+static void
+remove_quoting (WebKitDOMElement *element)
+{
+ WebKitDOMNodeList *list;
+ gint ii, length;
+
+ list = webkit_dom_element_query_selector_all (
+ element, "span.-x-evo-quoted", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ remove_node (webkit_dom_node_list_item (list, ii));
+ }
+
+ list = webkit_dom_element_query_selector_all (
+ element, "span.-x-evo-temp-text-wrapper", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *nd = webkit_dom_node_list_item (list, ii);
+
+ while (webkit_dom_node_has_child_nodes (nd)) {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (nd),
+ webkit_dom_node_get_first_child (nd),
+ nd,
+ NULL);
+ }
+
+ remove_node (nd);
+ }
+
+ webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+}
+
+static gboolean
+node_is_list (WebKitDOMNode *node)
+{
+ return node && (
+ WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node) ||
+ WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node));
+}
+
+static gint
+get_citation_level (WebKitDOMNode *node)
+{
+ WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+ gint level = 0;
+
+ while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+ webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
+ level++;
+
+ parent = webkit_dom_node_get_parent_node (parent);
+ }
+
+ return level;
+}
+
+static void
+format_change_block_to_block (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format,
+ EHTMLEditorView *view,
+ const gchar *value,
+ WebKitDOMDocument *document)
+{
+ gboolean after_selection_end, quoted = FALSE;
+ WebKitDOMElement *selection_start_marker, *selection_end_marker, *element;
+ WebKitDOMNode *block, *next_block;
+
+ e_html_editor_selection_save (selection);
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+ block = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+ if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-temp-text-wrapper")) {
+ block = webkit_dom_node_get_parent_node (block);
+ remove_wrapping (WEBKIT_DOM_ELEMENT (block));
+ remove_quoting (WEBKIT_DOM_ELEMENT (block));
+ quoted = TRUE;
+ }
+
+ /* Process all blocks that are in the selection one by one */
+ while (block) {
+ WebKitDOMNode *child;
+
+ after_selection_end = webkit_dom_node_contains (
+ block, WEBKIT_DOM_NODE (selection_end_marker));
+
+ next_block = webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (block));
+
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH)
+ element = e_html_editor_selection_get_paragraph_element (
+ selection, document, -1, 0);
+ else
+ element = webkit_dom_document_create_element (
+ document, value, NULL);
+
+ while ((child = webkit_dom_node_get_first_child (block)))
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element), child, NULL);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (block),
+ WEBKIT_DOM_NODE (element),
+ block,
+ NULL);
+
+ remove_node (block);
+
+ block = next_block;
+
+ if (after_selection_end)
+ break;
+ }
+
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH &&
+ !e_html_editor_view_get_html_mode (view)) {
+ gint citation_level, quote;
+
+ citation_level = get_citation_level (WEBKIT_DOM_NODE (element));
+ quote = citation_level ? citation_level + 1 : 0;
+
+ element = e_html_editor_selection_wrap_paragraph_length (
+ selection, element, selection->priv->word_wrap_length - quote);
+ }
+
+ if (quoted)
+ e_html_editor_view_quote_plain_text_element (view, element);
+
+ e_html_editor_selection_restore (selection);
+}
+
+static void
+remove_node_if_empty (WebKitDOMNode *node)
+{
+ if (!WEBKIT_DOM_IS_NODE (node))
+ return;
+
+ if (!webkit_dom_node_get_first_child (node)) {
+ remove_node (node);
+ } else {
+ gchar *text_content;
+
+ text_content = webkit_dom_node_get_text_content (node);
+ if (!text_content)
+ remove_node (node);
+
+ if (text_content && !*text_content)
+ remove_node (node);
+
+ g_free (text_content);
+ }
+}
+
+static void
+format_change_block_to_list (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format,
+ EHTMLEditorView *view,
+ WebKitDOMDocument *document)
+{
+ gboolean after_selection_end;
+ gboolean html_mode = e_html_editor_view_get_html_mode (view);
+ WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list;
+ WebKitDOMNode *block, *next_block;
+
+ e_html_editor_selection_save (selection);
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+ block = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+
+ list = create_list_element (selection, document, format, 0, html_mode);
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-temp-text-wrapper")) {
+ WebKitDOMElement *element;
+
+ block = webkit_dom_node_get_parent_node (block);
+
+ remove_wrapping (WEBKIT_DOM_ELEMENT (block));
+ remove_quoting (WEBKIT_DOM_ELEMENT (block));
+
+ e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+ element = webkit_dom_document_query_selector (
+ document, "body>br", NULL);
+
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+ WEBKIT_DOM_NODE (list),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+ block = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+ } else
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (block),
+ WEBKIT_DOM_NODE (list),
+ block,
+ NULL);
+
+ /* Process all blocks that are in the selection one by one */
+ while (block) {
+ gboolean empty = FALSE;
+ gchar *content;
+ WebKitDOMNode *child;
+
+ after_selection_end = webkit_dom_node_contains (
+ block, WEBKIT_DOM_NODE (selection_end_marker));
+
+ next_block = webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (block));
+
+ item = webkit_dom_document_create_element (document, "LI", NULL);
+ content = webkit_dom_node_get_text_content (block);
+
+ empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
+ g_free (content);
+
+ /* We have to use again the hidden space to move caret into newly inserted list */
+ if (empty) {
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (item),
+ UNICODE_ZERO_WIDTH_SPACE,
+ NULL);
+ }
+
+ while ((child = webkit_dom_node_get_first_child (block)))
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (item), child, NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL);
+
+ remove_node (block);
+
+ block = next_block;
+
+ if (after_selection_end)
+ break;
+ }
+
+ merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+
+ e_html_editor_view_force_spell_check (view);
+ e_html_editor_selection_restore (selection);
+}
+
+static void
+format_change_list_to_list (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format,
+ WebKitDOMDocument *document,
+ gboolean html_mode)
+{
+ EHTMLEditorSelectionBlockFormat prev = 0, next = 0;
+ gboolean done = FALSE, indented = FALSE;
+ gboolean selection_starts_in_first_child, selection_ends_in_last_child;
+ WebKitDOMElement *selection_start_marker, *selection_end_marker;
+ WebKitDOMNode *prev_list, *current_list, *next_list;
+
+ e_html_editor_selection_save (selection);
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+
+ current_list = webkit_dom_node_get_parent_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker)));
+
+ prev_list = webkit_dom_node_get_parent_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker)));
+
+ next_list = webkit_dom_node_get_parent_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_end_marker)));
+
+ selection_starts_in_first_child =
+ webkit_dom_node_contains (
+ webkit_dom_node_get_first_child (current_list),
+ WEBKIT_DOM_NODE (selection_start_marker));
+
+ selection_ends_in_last_child =
+ webkit_dom_node_contains (
+ webkit_dom_node_get_last_child (current_list),
+ WEBKIT_DOM_NODE (selection_end_marker));
+
+ indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented");
+
+ if (!prev_list || !next_list || indented) {
+ format_change_list_from_list (selection, document, format, html_mode);
+ goto out;
+ }
+
+ if (webkit_dom_node_is_same_node (prev_list, next_list)) {
+ prev_list = webkit_dom_node_get_previous_sibling (
+ webkit_dom_node_get_parent_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker))));
+ next_list = webkit_dom_node_get_next_sibling (
+ webkit_dom_node_get_parent_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_end_marker))));
+ if (!prev_list || !next_list) {
+ format_change_list_from_list (selection, document, format, html_mode);
+ goto out;
+ }
+ }
+
+ prev = e_html_editor_selection_get_list_format_from_node (prev_list);
+ next = e_html_editor_selection_get_list_format_from_node (next_list);
+
+ if (format == prev && format != -1 && prev != -1) {
+ if (selection_starts_in_first_child && selection_ends_in_last_child) {
+ done = TRUE;
+ merge_list_into_list (current_list, prev_list, FALSE);
+ }
+ }
+
+ if (format == next && format != -1 && next != -1) {
+ if (selection_starts_in_first_child && selection_ends_in_last_child) {
+ done = TRUE;
+ merge_list_into_list (next_list, prev_list, FALSE);
+ }
+ }
+
+ if (done)
+ goto out;
+
+ format_change_list_from_list (selection, document, format, html_mode);
+out:
+ e_html_editor_selection_restore (selection);
+}
+
+static void
+format_change_list_to_block (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format,
+ WebKitDOMDocument *document)
+{
+ gboolean after_end = FALSE;
+ WebKitDOMElement *selection_start, *element, *selection_end;
+ WebKitDOMNode *source_list, *next_item, *item, *source_list_clone;
+
+ e_html_editor_selection_save (selection);
+
+ selection_start = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+
+ item = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start));
+ source_list = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (item));
+ source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ WEBKIT_DOM_NODE (source_list_clone),
+ webkit_dom_node_get_next_sibling (source_list),
+ NULL);
+
+ next_item = item;
+
+ /* Process all nodes that are in selection one by one */
+ while (next_item) {
+ WebKitDOMNode *tmp;
+
+ tmp = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (next_item));
+
+ if (!after_end) {
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH)
+ element = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0);
+ else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE)
+ element = webkit_dom_document_create_element (document, "PRE", NULL);
+ else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)
+ element = webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL);
+ else
+ element = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0);
+
+ after_end = webkit_dom_node_contains (next_item, WEBKIT_DOM_NODE (selection_end));
+
+ while (webkit_dom_node_get_first_child (next_item)) {
+ WebKitDOMNode *node = webkit_dom_node_get_first_child (next_item);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element), node, NULL);
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ WEBKIT_DOM_NODE (element),
+ source_list_clone,
+ NULL);
+
+ remove_node (next_item);
+
+ next_item = tmp;
+ } else {
+ webkit_dom_node_append_child (
+ source_list_clone, next_item, NULL);
+
+ next_item = tmp;
+ }
+ }
+
+ remove_node_if_empty (source_list_clone);
+ remove_node_if_empty (source_list);
+
+ e_html_editor_selection_restore (selection);
+}
+
+/**
+ * e_html_editor_selection_set_block_format:
+ * @selection: an #EHTMLEditorSelection
+ * @format: an #EHTMLEditorSelectionBlockFormat value
+ *
+ * Changes block format of current paragraph to @format.
+ */
+void
+e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorSelectionBlockFormat current_format;
+ const gchar *value;
+ gboolean has_selection = FALSE;
+ gboolean from_list = FALSE, to_list = FALSE, html_mode;
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ current_format = e_html_editor_selection_get_block_format (selection);
+ if (current_format == format) {
+ return;
+ }
+
+ switch (format) {
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE:
+ value = "BLOCKQUOTE";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1:
+ value = "H1";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2:
+ value = "H2";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3:
+ value = "H3";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4:
+ value = "H4";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5:
+ value = "H5";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6:
+ value = "H6";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH:
+ value = "P";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE:
+ value = "PRE";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS:
+ value = "ADDRESS";
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST:
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
+ to_list = TRUE;
+ value = NULL;
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST:
+ to_list = TRUE;
+ value = NULL;
+ break;
+ case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE:
+ default:
+ value = NULL;
+ break;
+ }
+
+ if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0)
+ has_selection = TRUE;
+
+ /* H1 - H6 have bold font by default */
+ if (format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 &&
+ format <= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6)
+ selection->priv->is_bold = TRUE;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ html_mode = e_html_editor_view_get_html_mode (view);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ from_list =
+ current_format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range) {
+ g_object_unref (view);
+ return;
+ }
+
+ if (from_list && to_list)
+ format_change_list_to_list (selection, format, document, html_mode);
+
+ if (!from_list && !to_list)
+ format_change_block_to_block (selection, format, view, value, document);
+
+ if (from_list && !to_list)
+ format_change_list_to_block (selection, format, document);
+
+ if (!from_list && to_list)
+ format_change_block_to_list (selection, format, view, document);
+
+ if (!has_selection)
+ e_html_editor_view_force_spell_check (view);
+
+ g_object_unref (view);
+
+ /* When changing the format we need to re-set the alignment */
+ e_html_editor_selection_set_alignment (selection, selection->priv->alignment);
+
+ g_object_notify (G_OBJECT (selection), "block-format");
+}
+
+/**
+ * e_html_editor_selection_get_font_color:
+ * @selection: an #EHTMLEditorSelection
+ * @rgba: a #GdkRGBA object to be set to current font color
+ *
+ * Sets @rgba to contain color of current text selection or letter at current
+ * cursor position.
+ */
+void
+e_html_editor_selection_get_font_color (EHTMLEditorSelection *selection,
+ GdkRGBA *rgba)
+{
+ gchar *color;
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") == 0) {
+ color = g_strdup (selection->priv->font_color);
+ } else {
+ color = get_font_property (selection, "color");
+ if (!color) {
+ *rgba = black;
+ return;
+ }
+ }
+
+ gdk_rgba_parse (rgba, color);
+ g_free (color);
+}
+
+/**
+ * e_html_editor_selection_set_font_color:
+ * @selection: an #EHTMLEditorSelection
+ * @rgba: a #GdkRGBA
+ *
+ * Sets font color of current selection or letter at current cursor position to
+ * color defined in @rgba.
+ */
+void
+e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection,
+ const GdkRGBA *rgba)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+ guint32 rgba_value;
+ gchar *color;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (!rgba)
+ rgba = &black;
+
+ rgba_value = e_rgba_to_value ((GdkRGBA *) rgba);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR;
+ color = g_strdup_printf ("#%06x", rgba_value);
+ selection->priv->font_color = g_strdup (color);
+ e_html_editor_view_exec_command (view, command, color);
+ g_free (color);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "font-color");
+}
+
+/**
+ * e_html_editor_selection_get_font_name:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns name of font used in current selection or at letter at current cursor
+ * position.
+ *
+ * Returns: A string with font name. [transfer-none]
+ */
+const gchar *
+e_html_editor_selection_get_font_name (EHTMLEditorSelection *selection)
+{
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+ WebKitDOMCSSStyleDeclaration *css;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ range = html_editor_selection_get_current_range (selection);
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ g_free (selection->priv->font_family);
+ css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node));
+ selection->priv->font_family =
+ webkit_dom_css_style_declaration_get_property_value (css, "fontFamily");
+
+ return selection->priv->font_family;
+}
+
+/**
+ * e_html_editor_selection_set_font_name:
+ * @selection: an #EHTMLEditorSelection
+ * @font_name: a font name to apply
+ *
+ * Sets font name of current selection or of letter at current cursor position
+ * to @font_name.
+ */
+void
+e_html_editor_selection_set_font_name (EHTMLEditorSelection *selection,
+ const gchar *font_name)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME;
+ e_html_editor_view_exec_command (view, command, font_name);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "font-name");
+}
+
+/**
+ * e_editor_Selection_get_font_size:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns point size of current selection or of letter at current cursor position.
+ */
+ guint
+e_html_editor_selection_get_font_size (EHTMLEditorSelection *selection)
+{
+ gchar *size;
+ guint size_int;
+
+ g_return_val_if_fail (
+ E_IS_HTML_EDITOR_SELECTION (selection),
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL);
+
+ size = get_font_property (selection, "size");
+ if (!size)
+ return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+ size_int = atoi (size);
+ g_free (size);
+
+ if (size_int == 0)
+ return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+ return size_int;
+}
+
+/**
+ * e_html_editor_selection_set_font_size:
+ * @selection: an #EHTMLEditorSelection
+ * @font_size: point size to apply
+ *
+ * Sets font size of current selection or of letter at current cursor position
+ * to @font_size.
+ */
+void
+e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection,
+ guint font_size)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+ gchar *size_str;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ selection->priv->font_size = font_size;
+ command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE;
+ size_str = g_strdup_printf ("%d", font_size);
+ e_html_editor_view_exec_command (view, command, size_str);
+ g_free (size_str);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "font-size");
+}
+
+/**
+ * e_html_editor_selection_is_citation:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current paragraph is a citation.
+ *
+ * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_citation (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ if (WEBKIT_DOM_IS_TEXT (node))
+ return get_has_style (selection, "citation");
+
+ /* If we are changing the format of block we have to re-set bold property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return FALSE;
+ }
+ g_free (text_content);
+
+ value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+ /* citation == <blockquote type='cite'> */
+ if (g_strstr_len (value, -1, "cite"))
+ ret_val = TRUE;
+ else
+ ret_val = get_has_style (selection, "citation");
+
+ g_free (value);
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_is_indented:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current paragraph is indented. This does not include
+ * citations. To check, whether paragraph is a citation, use
+ * e_html_editor_selection_is_citation().
+ *
+ * Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_indented (EHTMLEditorSelection *selection)
+{
+ WebKitDOMRange *range;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (!WEBKIT_DOM_IS_ELEMENT (node))
+ node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+ element = webkit_dom_node_get_parent_element (node);
+
+ return element_has_class (element, "-x-evo-indented");
+}
+
+static gboolean
+is_in_html_mode (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view = e_html_editor_selection_ref_html_editor_view (selection);
+ gboolean ret_val;
+
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ ret_val = e_html_editor_view_get_html_mode (view);
+
+ g_object_unref (view);
+
+ return ret_val;
+}
+
+static gint
+get_list_level (WebKitDOMNode *node)
+{
+ gint level = 0;
+
+ while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
+ if (node_is_list (node))
+ level++;
+ node = webkit_dom_node_get_parent_node (node);
+ }
+
+ return level;
+}
+
+/**
+ * e_html_editor_selection_indent:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Indents current paragraph by one level.
+ */
+void
+e_html_editor_selection_indent (EHTMLEditorSelection *selection)
+{
+ gboolean has_selection;
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ has_selection = g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0;
+
+ if (!has_selection) {
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ WebKitDOMNode *node, *clone;
+ WebKitDOMElement *element, *caret_position;
+ gint word_wrap_length = selection->priv->word_wrap_length;
+ gint level;
+ gint final_width = 0;
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+
+ caret_position = e_html_editor_selection_save_caret_position (selection);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range) {
+ g_object_unref (view);
+ return;
+ }
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (!WEBKIT_DOM_IS_ELEMENT (node))
+ node = WEBKIT_DOM_NODE (
+ webkit_dom_node_get_parent_element (node));
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) {
+ gboolean html_mode = e_html_editor_view_get_html_mode (view);
+ WebKitDOMElement *list;
+ WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (node);
+ EHTMLEditorSelectionBlockFormat format;
+
+ format = e_html_editor_selection_get_list_format_from_node (source_list);
+
+ list = create_list_element (
+ selection, document, format, get_list_level (node), html_mode);
+
+ element_add_class (list, "-x-evo-indented");
+ webkit_dom_node_insert_before (
+ source_list, WEBKIT_DOM_NODE (list), node, NULL);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (list), node, NULL);
+ if (!webkit_dom_node_contains (node, WEBKIT_DOM_NODE (caret_position))) {
+ webkit_dom_node_append_child (
+ node, WEBKIT_DOM_NODE (caret_position), NULL);
+ }
+
+ merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+
+ e_html_editor_selection_restore_caret_position (selection);
+
+ g_object_unref (view);
+ return;
+ }
+
+ level = get_indentation_level (WEBKIT_DOM_ELEMENT (node));
+
+ final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
+ if (final_width < 10 && !is_in_html_mode (selection)) {
+ e_html_editor_selection_restore_caret_position (selection);
+ g_object_unref (view);
+ return;
+ }
+
+ element = webkit_dom_node_get_parent_element (node);
+ clone = webkit_dom_node_clone_node (node, TRUE);
+
+ /* Remove style and let the paragraph inherit it from parent */
+ if (element_has_class (WEBKIT_DOM_ELEMENT (clone), "-x-evo-paragraph"))
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (clone), "style");
+
+ element = e_html_editor_selection_get_indented_element (
+ selection, document, final_width);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element),
+ clone,
+ NULL);
+
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ node,
+ NULL);
+
+ e_html_editor_selection_restore_caret_position (selection);
+ } else {
+ command = E_HTML_EDITOR_VIEW_COMMAND_INDENT;
+ e_html_editor_selection_save (selection);
+ e_html_editor_view_exec_command (view, command, NULL);
+ }
+
+ e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+ if (has_selection)
+ e_html_editor_selection_restore (selection);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "indented");
+}
+
+static const gchar *
+get_css_alignment_value (EHTMLEditorSelectionAlignment alignment)
+{
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+ return ""; /* Left is by default on ltr */
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER)
+ return "text-align: center;";
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT)
+ return "text-align: right;";
+
+ return "";
+}
+
+static void
+unindent_list (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document)
+{
+ gboolean after = FALSE;
+ WebKitDOMElement *new_list;
+ WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+ WebKitDOMElement *selection_start_marker;
+ WebKitDOMElement *selection_end_marker;
+
+ selection_start_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-start-marker", NULL);
+ selection_end_marker = webkit_dom_document_query_selector (
+ document, "span#-x-evo-selection-end-marker", NULL);
+
+ if (!selection_start_marker || !selection_end_marker)
+ return;
+
+ /* Copy elements from previous block to list */
+ item = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (selection_start_marker));
+ source_list = webkit_dom_node_get_parent_node (item);
+ new_list = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_clone_node (source_list, FALSE));
+ current_list = source_list;
+ source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ WEBKIT_DOM_NODE (source_list_clone),
+ webkit_dom_node_get_next_sibling (source_list),
+ NULL);
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+ element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+ while (item) {
+ WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+ if (after) {
+ webkit_dom_node_append_child (
+ source_list_clone, WEBKIT_DOM_NODE (item), NULL);
+ } else {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (source_list),
+ item,
+ webkit_dom_node_get_next_sibling (source_list),
+ NULL);
+ }
+ }
+
+ if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker)))
+ after = TRUE;
+
+ if (!next_item) {
+ if (after)
+ break;
+
+ current_list = webkit_dom_node_get_next_sibling (current_list);
+ next_item = webkit_dom_node_get_first_child (current_list);
+ }
+ item = next_item;
+ }
+
+ remove_node_if_empty (source_list_clone);
+ remove_node_if_empty (source_list);
+}
+/**
+ * e_html_editor_selection_unindent:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Unindents current paragraph by one level.
+ */
+void
+e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
+{
+ gboolean has_selection;
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ has_selection = g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0;
+
+ if (!has_selection) {
+ EHTMLEditorSelectionAlignment alignment;
+ gboolean before_node = TRUE, reinsert_caret_position = FALSE;
+ const gchar *align_value;
+ gint word_wrap_length = selection->priv->word_wrap_length;
+ gint level, width;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+ WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL;
+ WebKitDOMNode *node, *clone, *node_clone, *caret_node;
+ WebKitDOMRange *range;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ e_html_editor_selection_save_caret_position (selection);
+
+ alignment = e_html_editor_selection_get_alignment (selection);
+ align_value = get_css_alignment_value (alignment);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range) {
+ g_object_unref (view);
+ return;
+ }
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (!WEBKIT_DOM_IS_ELEMENT (node))
+ node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+ element = webkit_dom_node_get_parent_element (node);
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) {
+ e_html_editor_selection_save (selection);
+ e_html_editor_selection_clear_caret_position_marker (selection);
+
+ unindent_list (selection, document);
+ e_html_editor_selection_restore (selection);
+ g_object_unref (view);
+ return;
+ }
+
+ if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element))
+ return;
+
+ element_add_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-to-unindent");
+
+ level = get_indentation_level (element);
+ width = word_wrap_length - SPACES_PER_INDENTATION * level;
+ clone = WEBKIT_DOM_NODE (webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE));
+
+ /* Look if we have previous siblings, if so, we have to
+ * create new blockquote that will include them */
+ if (webkit_dom_node_get_previous_sibling (node))
+ prev_blockquote = e_html_editor_selection_get_indented_element (
+ selection, document, width);
+
+ /* Look if we have next siblings, if so, we have to
+ * create new blockquote that will include them */
+ if (webkit_dom_node_get_next_sibling (node))
+ next_blockquote = e_html_editor_selection_get_indented_element (
+ selection, document, width);
+
+ /* Copy nodes that are before / after the element that we want to unindent */
+ while (webkit_dom_node_has_child_nodes (clone)) {
+ WebKitDOMNode *child;
+
+ child = webkit_dom_node_get_first_child (clone);
+
+ if (is_caret_position_node (child)) {
+ reinsert_caret_position = TRUE;
+ caret_node = webkit_dom_node_clone_node (child, TRUE);
+ remove_node (child);
+ continue;
+ }
+
+ if (webkit_dom_node_is_equal_node (child, node)) {
+ before_node = FALSE;
+ node_clone = webkit_dom_node_clone_node (child, TRUE);
+ remove_node (child);
+ continue;
+ }
+
+ webkit_dom_node_append_child (
+ before_node ?
+ WEBKIT_DOM_NODE (prev_blockquote) :
+ WEBKIT_DOM_NODE (next_blockquote),
+ child,
+ NULL);
+
+ remove_node (child);
+ }
+
+ element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent");
+
+ /* Insert blockqoute with nodes that were before the element that we want to unindent */
+ if (prev_blockquote) {
+ if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+ WEBKIT_DOM_NODE (prev_blockquote),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ }
+
+ /* Reinsert the caret position */
+ if (reinsert_caret_position) {
+ webkit_dom_node_insert_before (
+ node_clone,
+ caret_node,
+ webkit_dom_node_get_first_child (node_clone),
+ NULL);
+ }
+
+ if (level == 1 && element_has_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-paragraph"))
+ e_html_editor_selection_set_paragraph_style (
+ selection, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, align_value);
+
+ /* Insert the unindented element */
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+ node_clone,
+ WEBKIT_DOM_NODE (element),
+ NULL);
+
+ /* Insert blockqoute with nodes that were after the element that we want to unindent */
+ if (next_blockquote) {
+ if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+ WEBKIT_DOM_NODE (next_blockquote),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ }
+
+ /* Remove old blockquote */
+ remove_node (WEBKIT_DOM_NODE (element));
+
+ e_html_editor_selection_restore_caret_position (selection);
+ } else {
+ command = E_HTML_EDITOR_VIEW_COMMAND_OUTDENT;
+ e_html_editor_selection_save (selection);
+ e_html_editor_view_exec_command (view, command, NULL);
+ }
+
+ e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+ if (has_selection)
+ e_html_editor_selection_restore (selection);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "indented");
+}
+
+/**
+ * e_html_editor_selection_is_bold:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is bold.
+ *
+ * Returns @TRUE when selection is bold, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_bold (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ EHTMLEditorView *view;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ /* If we are changing the format of block we have to re-set bold property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return selection->priv->is_bold;
+ }
+ g_free (text_content);
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "font-weight");
+
+ if (g_strstr_len (value, -1, "normal"))
+ ret_val = FALSE;
+ else
+ ret_val = TRUE;
+
+ g_free (value);
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_bold:
+ * @selection: an #EHTMLEditorSelection
+ * @bold: @TRUE to enable bold, @FALSE to disable
+ *
+ * Toggles bold formatting of current selection or letter at current cursor
+ * position, depending on whether @bold is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_bold (EHTMLEditorSelection *selection,
+ gboolean bold)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_bold (selection) == bold)
+ return;
+
+ selection->priv->is_bold = bold;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_BOLD;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "bold");
+}
+
+/**
+ * e_html_editor_selection_is_italic:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is italic.
+ *
+ * Returns @TRUE when selection is italic, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_italic (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ EHTMLEditorView *view;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ /* If we are changing the format of block we have to re-set italic property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return selection->priv->is_italic;
+ }
+ g_free (text_content);
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "font-style");
+
+ if (g_strstr_len (value, -1, "italic"))
+ ret_val = TRUE;
+ else
+ ret_val = FALSE;
+
+ g_free (value);
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_italic:
+ * @selection: an #EHTMLEditorSelection
+ * @italic: @TRUE to enable italic, @FALSE to disable
+ *
+ * Toggles italic formatting of current selection or letter at current cursor
+ * position, depending on whether @italic is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_italic (EHTMLEditorSelection *selection,
+ gboolean italic)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_italic (selection) == italic)
+ return;
+
+ selection->priv->is_italic = italic;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_ITALIC;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "italic");
+}
+
+static gboolean
+is_monospaced_element (WebKitDOMElement *element)
+{
+ gchar *value;
+ gboolean ret_val = FALSE;
+
+ if (!element)
+ return FALSE;
+
+ if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element))
+ return FALSE;
+
+ value = webkit_dom_element_get_attribute (element, "face");
+
+ if (g_strcmp0 (value, "monospace") == 0)
+ ret_val = TRUE;
+
+ g_free (value);
+
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_is_monospaced:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is monospaced.
+ *
+ * Returns @TRUE when selection is monospaced, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_monospaced (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ EHTMLEditorView *view;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ /* If we are changing the format of block we have to re-set italic property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return selection->priv->is_monospaced;
+ }
+ g_free (text_content);
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "font-family");
+
+ if (g_strstr_len (value, -1, "monospace"))
+ ret_val = TRUE;
+ else
+ ret_val = FALSE;
+
+ g_free (value);
+ return ret_val;
+}
+
+static void
+move_caret_into_element (WebKitDOMDocument *document,
+ WebKitDOMElement *element)
+{
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *window_selection;
+ WebKitDOMRange *new_range;
+
+ if (!element)
+ return;
+
+ window = webkit_dom_document_get_default_view (document);
+ window_selection = webkit_dom_dom_window_get_selection (window);
+ new_range = webkit_dom_document_create_range (document);
+
+ webkit_dom_range_select_node_contents (
+ new_range, WEBKIT_DOM_NODE (element), NULL);
+ webkit_dom_range_collapse (new_range, FALSE, NULL);
+ webkit_dom_dom_selection_remove_all_ranges (window_selection);
+ webkit_dom_dom_selection_add_range (window_selection, new_range);
+}
+
+/**
+ * e_html_editor_selection_set_monospaced:
+ * @selection: an #EHTMLEditorSelection
+ * @monospaced: @TRUE to enable monospaced, @FALSE to disable
+ *
+ * Toggles monospaced formatting of current selection or letter at current cursor
+ * position, depending on whether @monospaced is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
+ gboolean monospaced)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *window_selection;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_monospaced (selection) == monospaced)
+ return;
+
+ selection->priv->is_monospaced = monospaced;
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ document = webkit_web_view_get_dom_document (web_view);
+ window = webkit_dom_document_get_default_view (document);
+ window_selection = webkit_dom_dom_window_get_selection (window);
+
+ if (monospaced) {
+ gchar *font_size_str;
+ guint font_size;
+ WebKitDOMElement *monospace;
+
+ monospace = webkit_dom_document_create_element (
+ document, "font", NULL);
+ webkit_dom_element_set_attribute (
+ monospace, "face", "monospace", NULL);
+
+ font_size = selection->priv->font_size;
+ if (font_size == 0)
+ font_size = E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+ font_size_str = g_strdup_printf ("%d", font_size);
+ webkit_dom_element_set_attribute (
+ monospace, "size", font_size_str, NULL);
+ g_free (font_size_str);
+
+ if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0) {
+ gchar *html, *outer_html;
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (monospace),
+ WEBKIT_DOM_NODE (
+ webkit_dom_range_clone_contents (range, NULL)),
+ NULL);
+
+ outer_html = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (monospace));
+
+ html = g_strconcat (
+ /* Mark selection for restoration */
+ "<span id=\"-x-evo-selection-start-marker\"></span>",
+ outer_html,
+ "<span id=\"-x-evo-selection-end-marker\"></span>",
+ NULL),
+
+ e_html_editor_selection_insert_html (selection, html);
+
+ e_html_editor_selection_restore (selection);
+
+ g_free (html);
+ g_free (outer_html);
+ } else {
+ /* https://bugs.webkit.org/show_bug.cgi?id=15256 */
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (monospace),
+ UNICODE_ZERO_WIDTH_SPACE,
+ NULL);
+ webkit_dom_range_insert_node (
+ range, WEBKIT_DOM_NODE (monospace), NULL);
+
+ move_caret_into_element (document, monospace);
+ }
+ } else {
+ gboolean is_bold, is_italic, is_underline, is_strikethrough;
+ guint font_size;
+ WebKitDOMElement *tt_element;
+ WebKitDOMNode *node;
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (WEBKIT_DOM_IS_ELEMENT (node) &&
+ is_monospaced_element (WEBKIT_DOM_ELEMENT (node))) {
+ tt_element = WEBKIT_DOM_ELEMENT (node);
+ } else {
+ tt_element = e_html_editor_dom_node_find_parent_element (node, "FONT");
+
+ if (!is_monospaced_element (tt_element)) {
+ g_object_unref (view);
+ return;
+ }
+ }
+
+ /* Save current formatting */
+ is_bold = selection->priv->is_bold;
+ is_italic = selection->priv->is_italic;
+ is_underline = selection->priv->is_underline;
+ is_strikethrough = selection->priv->is_strikethrough;
+ font_size = selection->priv->font_size;
+ if (font_size == 0)
+ font_size = E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+ if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0) {
+ gchar *html, *outer_html, *inner_html, *beginning, *end;
+ gchar *start_position, *end_position, *font_size_str;
+ WebKitDOMElement *wrapper;
+
+ wrapper = webkit_dom_document_create_element (
+ document, "SPAN", NULL);
+ webkit_dom_element_set_id (wrapper, "-x-evo-remove-tt");
+ webkit_dom_range_surround_contents (
+ range, WEBKIT_DOM_NODE (wrapper), NULL);
+
+ html = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (tt_element));
+
+ start_position = g_strstr_len (
+ html, -1, "<span id=\"-x-evo-remove-tt\"");
+ end_position = g_strstr_len (start_position, -1, "</span>");
+
+ beginning = g_utf8_substring (
+ html, 0, g_utf8_pointer_to_offset (html, start_position));
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (wrapper));
+ end = g_utf8_substring (
+ html,
+ g_utf8_pointer_to_offset (html, end_position) + 7,
+ g_utf8_strlen (html, -1)),
+
+ font_size_str = g_strdup_printf ("%d", font_size);
+
+ outer_html =
+ g_strconcat (
+ /* Beginning */
+ beginning,
+ /* End the previous FONT tag */
+ "</font>",
+ /* Mark selection for restoration */
+ "<span id=\"-x-evo-selection-start-marker\"></span>",
+ /* Inside will be the same */
+ inner_html,
+ "<span id=\"-x-evo-selection-end-marker\"></span>",
+ /* Start the new FONT element */
+ "<font face=\"monospace\" size=\"",
+ font_size_str,
+ "\">",
+ /* End - we have to start after </span> */
+ end,
+ NULL),
+
+ g_free (font_size_str);
+
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (tt_element),
+ outer_html,
+ NULL);
+
+ e_html_editor_selection_restore (selection);
+
+ g_free (html);
+ g_free (outer_html);
+ g_free (inner_html);
+ g_free (beginning);
+ g_free (end);
+ } else {
+ WebKitDOMRange *new_range;
+ gchar *outer_html;
+ gchar *tmp;
+
+ webkit_dom_element_set_id (tt_element, "ev-tt");
+
+ outer_html = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (tt_element));
+ tmp = g_strconcat (outer_html, UNICODE_ZERO_WIDTH_SPACE, NULL);
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (tt_element),
+ tmp, NULL);
+
+ /* We need to get that element again */
+ tt_element = webkit_dom_document_get_element_by_id (
+ document, "ev-tt");
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (tt_element), "id");
+
+ new_range = webkit_dom_document_create_range (document);
+ webkit_dom_range_set_start_after (
+ new_range, WEBKIT_DOM_NODE (tt_element), NULL);
+ webkit_dom_range_set_end_after (
+ new_range, WEBKIT_DOM_NODE (tt_element), NULL);
+
+ webkit_dom_dom_selection_remove_all_ranges (
+ window_selection);
+ webkit_dom_dom_selection_add_range (
+ window_selection, new_range);
+
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "right", "character");
+
+ g_free (outer_html);
+ g_free (tmp);
+
+ e_html_editor_view_force_spell_check_for_current_paragraph (
+ view);
+ }
+
+ /* Re-set formatting */
+ if (is_bold)
+ e_html_editor_selection_set_bold (selection, TRUE);
+ if (is_italic)
+ e_html_editor_selection_set_italic (selection, TRUE);
+ if (is_underline)
+ e_html_editor_selection_set_underline (selection, TRUE);
+ if (is_strikethrough)
+ e_html_editor_selection_set_strikethrough (selection, TRUE);
+
+ e_html_editor_selection_set_font_size (selection, font_size);
+ }
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "monospaced");
+}
+
+/**
+ * e_html_editor_selection_is_strikethrough:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is striked through.
+ *
+ * Returns @TRUE when selection is striked through, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_strikethrough (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ EHTMLEditorView *view;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ /* If we are changing the format of block we have to re-set strikethrough property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return selection->priv->is_strikethrough;
+ }
+ g_free (text_content);
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "text-decoration");
+
+ if (g_strstr_len (value, -1, "line-through"))
+ ret_val = TRUE;
+ else
+ ret_val = get_has_style (selection, "strike");
+
+ g_free (value);
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_strikethrough:
+ * @selection: an #EHTMLEditorSelection
+ * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
+ *
+ * Toggles strike through formatting of current selection or letter at current
+ * cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_strikethrough (EHTMLEditorSelection *selection,
+ gboolean strikethrough)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_strikethrough (selection) == strikethrough)
+ return;
+
+ selection->priv->is_strikethrough = strikethrough;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "strikethrough");
+}
+
+/**
+ * e_html_editor_selection_is_subscript:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in subscript.
+ *
+ * Returns @TRUE when selection is in subscript, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_subscript (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ g_object_unref (view);
+
+ range = html_editor_selection_get_current_range (selection);
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ while (node) {
+ gchar *tag_name;
+
+ tag_name = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (node));
+
+ if (g_ascii_strncasecmp (tag_name, "sub", 3) == 0) {
+ g_free (tag_name);
+ break;
+ }
+
+ g_free (tag_name);
+ node = webkit_dom_node_get_parent_node (node);
+ }
+
+ return (node != NULL);
+}
+
+/**
+ * e_html_editor_selection_set_subscript:
+ * @selection: an #EHTMLEditorSelection
+ * @subscript: @TRUE to enable subscript, @FALSE to disable
+ *
+ * Toggles subscript of current selection or letter at current cursor position,
+ * depending on whether @subscript is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_subscript (EHTMLEditorSelection *selection,
+ gboolean subscript)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_subscript (selection) == subscript)
+ return;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "subscript");
+}
+
+/**
+ * e_html_editor_selection_is_superscript:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in superscript.
+ *
+ * Returns @TRUE when selection is in superscript, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_superscript (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ g_object_unref (view);
+
+ range = html_editor_selection_get_current_range (selection);
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ while (node) {
+ gchar *tag_name;
+
+ tag_name = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (node));
+
+ if (g_ascii_strncasecmp (tag_name, "sup", 3) == 0) {
+ g_free (tag_name);
+ break;
+ }
+
+ g_free (tag_name);
+ node = webkit_dom_node_get_parent_node (node);
+ }
+
+ return (node != NULL);
+}
+
+/**
+ * e_html_editor_selection_set_superscript:
+ * @selection: an #EHTMLEditorSelection
+ * @superscript: @TRUE to enable superscript, @FALSE to disable
+ *
+ * Toggles superscript of current selection or letter at current cursor position,
+ * depending on whether @superscript is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_superscript (EHTMLEditorSelection *selection,
+ gboolean superscript)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_superscript (selection) == superscript)
+ return;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "superscript");
+}
+
+/**
+ * e_html_editor_selection_is_underline:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is underlined.
+ *
+ * Returns @TRUE when selection is underlined, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_underline (EHTMLEditorSelection *selection)
+{
+ gboolean ret_val;
+ gchar *value, *text_content;
+ EHTMLEditorView *view;
+ WebKitDOMCSSStyleDeclaration *style;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+ WebKitDOMRange *range;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!e_html_editor_view_get_html_mode (view)) {
+ g_object_unref (view);
+ return FALSE;
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+ window = webkit_dom_document_get_default_view (document);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return FALSE;
+
+ node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+ /* If we are changing the format of block we have to re-set underline property,
+ * otherwise it will be turned off because of no text in composer */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_content, "") == 0) {
+ g_free (text_content);
+ return selection->priv->is_underline;
+ }
+ g_free (text_content);
+
+ if (WEBKIT_DOM_IS_ELEMENT (node))
+ element = WEBKIT_DOM_ELEMENT (node);
+ else
+ element = webkit_dom_node_get_parent_element (node);
+
+ style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+ value = webkit_dom_css_style_declaration_get_property_value (style, "text-decoration");
+
+ if (g_strstr_len (value, -1, "underline"))
+ ret_val = TRUE;
+ else
+ ret_val = get_has_style (selection, "u");
+
+ g_free (value);
+ return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_underline:
+ * @selection: an #EHTMLEditorSelection
+ * @underline: @TRUE to enable underline, @FALSE to disable
+ *
+ * Toggles underline formatting of current selection or letter at current
+ * cursor position, depending on whether @underline is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_underline (EHTMLEditorSelection *selection,
+ gboolean underline)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ if (e_html_editor_selection_is_underline (selection) == underline)
+ return;
+
+ selection->priv->is_underline = underline;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE;
+ e_html_editor_view_exec_command (view, command, NULL);
+
+ g_object_unref (view);
+
+ g_object_notify (G_OBJECT (selection), "underline");
+}
+
+/**
+ * e_html_editor_selection_unlink:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Removes any links (&lt;A&gt; elements) from current selection or at current
+ * cursor position.
+ */
+void
+e_html_editor_selection_unlink (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range;
+ WebKitDOMElement *link;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+
+ range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ link = e_html_editor_dom_node_find_parent_element (
+ webkit_dom_range_get_start_container (range, NULL), "A");
+
+ if (!link) {
+ gchar *text;
+ /* get element that was clicked on */
+ link = e_html_editor_view_get_element_under_mouse_click (view);
+ if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+ link = NULL;
+
+ text = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (link));
+ webkit_dom_html_element_set_outer_html (WEBKIT_DOM_HTML_ELEMENT (link), text, NULL);
+ g_free (text);
+ } else {
+ command = E_HTML_EDITOR_VIEW_COMMAND_UNLINK;
+ e_html_editor_view_exec_command (view, command, NULL);
+ }
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_create_link:
+ * @selection: an #EHTMLEditorSelection
+ * @uri: destination of the new link
+ *
+ * Converts current selection into a link pointing to @url.
+ */
+void
+e_html_editor_selection_create_link (EHTMLEditorSelection *selection,
+ const gchar *uri)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (uri != NULL && *uri != '\0');
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK;
+ e_html_editor_view_exec_command (view, command, uri);
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_insert_text:
+ * @selection: an #EHTMLEditorSelection
+ * @plain_text: text to insert
+ *
+ * Inserts @plain_text at current cursor position. When a text range is selected,
+ * it will be replaced by @plain_text.
+ */
+void
+e_html_editor_selection_insert_text (EHTMLEditorSelection *selection,
+ const gchar *plain_text)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (plain_text != NULL);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT;
+ e_html_editor_view_exec_command (view, command, plain_text);
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_insert_html:
+ * @selection: an #EHTMLEditorSelection
+ * @html_text: an HTML code to insert
+ *
+ * Insert @html_text into document at current cursor position. When a text range
+ * is selected, it will be replaced by @html_text.
+ */
+void
+e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
+ const gchar *html_text)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorViewCommand command;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (html_text != NULL);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML;
+ if (e_html_editor_view_get_html_mode (view)) {
+ e_html_editor_view_exec_command (view, command, html_text);
+ } else {
+ e_html_editor_view_convert_and_insert_html_to_plain_text (
+ view, html_text);
+ }
+
+ g_object_unref (view);
+}
+
+
+/************************* image_load_and_insert_async() *************************/
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+ EHTMLEditorSelection *selection;
+ WebKitDOMElement *element;
+ GInputStream *input_stream;
+ GOutputStream *output_stream;
+ GFile *file;
+ GFileInfo *file_info;
+ goffset total_num_bytes;
+ gssize bytes_read;
+ const gchar *content_type;
+ const gchar *filename;
+ gchar buffer[4096];
+};
+
+/* Forward Declaration */
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ LoadContext *load_context);
+
+static LoadContext *
+image_load_context_new (EHTMLEditorSelection *selection)
+{
+ LoadContext *load_context;
+
+ load_context = g_slice_new0 (LoadContext);
+ load_context->selection = selection;
+
+ return load_context;
+}
+
+static void
+image_load_context_free (LoadContext *load_context)
+{
+ if (load_context->input_stream != NULL)
+ g_object_unref (load_context->input_stream);
+
+ if (load_context->output_stream != NULL)
+ g_object_unref (load_context->output_stream);
+
+ if (load_context->file_info != NULL)
+ g_object_unref (load_context->file_info);
+
+ if (load_context->file != NULL)
+ g_object_unref (load_context->file);
+
+ g_slice_free (LoadContext, load_context);
+}
+
+static void
+replace_base64_image_src (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ const gchar *base64_content,
+ const gchar *filename,
+ const gchar *uri)
+{
+ EHTMLEditorView *view;
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ e_html_editor_view_set_changed (view, TRUE);
+ g_object_unref (view);
+
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+ base64_content);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-name",
+ filename ? filename : "", NULL);
+}
+
+static void
+insert_base64_image (EHTMLEditorSelection *selection,
+ const gchar *base64_content,
+ const gchar *filename,
+ const gchar *uri)
+{
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element, *caret_position, *resizable_wrapper;
+ WebKitDOMText *text;
+
+ caret_position = e_html_editor_selection_save_caret_position (selection);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+
+ e_html_editor_view_set_changed (view, TRUE);
+ g_object_unref (view);
+
+ resizable_wrapper =
+ webkit_dom_document_create_element (document, "span", NULL);
+ webkit_dom_element_set_attribute (
+ resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL);
+
+ element = webkit_dom_document_create_element (document, "img", NULL);
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+ base64_content);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-name",
+ filename ? filename : "", NULL);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (resizable_wrapper),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (caret_position)),
+ WEBKIT_DOM_NODE (resizable_wrapper),
+ WEBKIT_DOM_NODE (caret_position),
+ NULL);
+
+ /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
+ * caret on right position */
+ text = webkit_dom_document_create_text_node (
+ document, UNICODE_ZERO_WIDTH_SPACE);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (caret_position)),
+ WEBKIT_DOM_NODE (text),
+ WEBKIT_DOM_NODE (caret_position),
+ NULL);
+
+ e_html_editor_selection_restore_caret_position (selection);
+}
+
+static void
+image_load_finish (LoadContext *load_context)
+{
+ EHTMLEditorSelection *selection;
+ GMemoryOutputStream *output_stream;
+ gchar *base64_encoded, *mime_type, *output, *uri;
+ gsize size;
+ gpointer data;
+
+ output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+
+ selection = load_context->selection;
+
+ mime_type = g_content_type_get_mime_type (load_context->content_type);
+
+ data = g_memory_output_stream_get_data (output_stream);
+ size = g_memory_output_stream_get_data_size (output_stream);
+ uri = g_file_get_uri (load_context->file);
+
+ base64_encoded = g_base64_encode ((const guchar *) data, size);
+ output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+ if (load_context->element)
+ replace_base64_image_src (
+ selection, load_context->element, output, load_context->filename, uri);
+ else
+ insert_base64_image (selection, output, load_context->filename, uri);
+
+ g_free (base64_encoded);
+ g_free (output);
+ g_free (mime_type);
+ g_free (uri);
+
+ image_load_context_free (load_context);
+}
+
+static void
+image_load_write_cb (GOutputStream *output_stream,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GInputStream *input_stream;
+ gssize bytes_written;
+ GError *error = NULL;
+
+ bytes_written = g_output_stream_write_finish (
+ output_stream, result, &error);
+
+ if (error) {
+ image_load_context_free (load_context);
+ return;
+ }
+
+ input_stream = load_context->input_stream;
+
+ if (bytes_written < load_context->bytes_read) {
+ g_memmove (
+ load_context->buffer,
+ load_context->buffer + bytes_written,
+ load_context->bytes_read - bytes_written);
+ load_context->bytes_read -= bytes_written;
+
+ g_output_stream_write_async (
+ output_stream,
+ load_context->buffer,
+ load_context->bytes_read,
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) image_load_write_cb,
+ load_context);
+ } else
+ g_input_stream_read_async (
+ input_stream,
+ load_context->buffer,
+ sizeof (load_context->buffer),
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) image_load_stream_read_cb,
+ load_context);
+}
+
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GOutputStream *output_stream;
+ gssize bytes_read;
+ GError *error = NULL;
+
+ bytes_read = g_input_stream_read_finish (
+ input_stream, result, &error);
+
+ if (error) {
+ image_load_context_free (load_context);
+ return;
+ }
+
+ if (bytes_read == 0) {
+ image_load_finish (load_context);
+ return;
+ }
+
+ output_stream = load_context->output_stream;
+ load_context->bytes_read = bytes_read;
+
+ g_output_stream_write_async (
+ output_stream,
+ load_context->buffer,
+ load_context->bytes_read,
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) image_load_write_cb,
+ load_context);
+}
+
+static void
+image_load_file_read_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ GError *error = NULL;
+
+ /* Input stream might be NULL, so don't use cast macro. */
+ input_stream = g_file_read_finish (file, result, &error);
+ load_context->input_stream = (GInputStream *) input_stream;
+
+ if (error) {
+ image_load_context_free (load_context);
+ return;
+ }
+
+ /* Load the contents into a GMemoryOutputStream. */
+ output_stream = g_memory_output_stream_new (
+ NULL, 0, g_realloc, g_free);
+
+ load_context->output_stream = output_stream;
+
+ g_input_stream_read_async (
+ load_context->input_stream,
+ load_context->buffer,
+ sizeof (load_context->buffer),
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) image_load_stream_read_cb,
+ load_context);
+}
+
+static void
+image_load_query_info_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ file_info = g_file_query_info_finish (file, result, &error);
+ if (error) {
+ image_load_context_free (load_context);
+ return;
+ }
+
+ load_context->content_type = g_file_info_get_content_type (file_info);
+ load_context->total_num_bytes = g_file_info_get_size (file_info);
+ load_context->filename = g_file_info_get_name (file_info);
+
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT,
+ NULL, (GAsyncReadyCallback)
+ image_load_file_read_cb, load_context);
+}
+
+static void
+image_load_and_insert_async (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ const gchar *uri)
+{
+ LoadContext *load_context;
+ GFile *file;
+
+ g_return_if_fail (uri && *uri);
+
+ file = g_file_new_for_uri (uri);
+ g_return_if_fail (file != NULL);
+
+ load_context = image_load_context_new (selection);
+ load_context->file = file;
+ load_context->element = element;
+
+ g_file_query_info_async (
+ file, "standard::*",
+ G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
+ NULL, (GAsyncReadyCallback)
+ image_load_query_info_cb, load_context);
+}
+
+/**
+ * e_html_editor_selection_insert_image:
+ * @selection: an #EHTMLEditorSelection
+ * @image_uri: an URI of the source image
+ *
+ * Inserts image at current cursor position using @image_uri as source. When a
+ * text range is selected, it will be replaced by the image.
+ */
+void
+e_html_editor_selection_insert_image (EHTMLEditorSelection *selection,
+ const gchar *image_uri)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (image_uri != NULL);
+
+ if (is_in_html_mode (selection)) {
+ if (strstr (image_uri, ";base64,")) {
+ if (g_str_has_prefix (image_uri, "data:"))
+ insert_base64_image (selection, image_uri, "", "");
+ if (strstr (image_uri, ";data")) {
+ const gchar *base64_data = strstr (image_uri, ";") + 1;
+ gchar *filename;
+ glong filename_length;
+
+ filename_length =
+ g_utf8_strlen (image_uri, -1) -
+ g_utf8_strlen (base64_data, -1) - 1;
+ filename = g_strndup (image_uri, filename_length);
+
+ insert_base64_image (selection, base64_data, filename, "");
+ g_free (filename);
+ }
+ } else
+ image_load_and_insert_async (selection, NULL, image_uri);
+ }
+}
+
+/**
+ * e_html_editor_selection_replace_image_src:
+ * @selection: an #EHTMLEditorSelection
+ * @image: #WebKitDOMElement representation of image
+ * @image_uri: an URI of the source image
+ *
+ * Replace the src attribute of the given @image with @image_uri.
+ */
+void
+e_html_editor_selection_replace_image_src (EHTMLEditorSelection *selection,
+ WebKitDOMElement *image,
+ const gchar *image_uri)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+ g_return_if_fail (image_uri != NULL);
+ g_return_if_fail (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image));
+
+ if (strstr (image_uri, ";base64,")) {
+ if (g_str_has_prefix (image_uri, "data:"))
+ replace_base64_image_src (
+ selection, image, image_uri, "", "");
+ if (strstr (image_uri, ";data")) {
+ const gchar *base64_data = strstr (image_uri, ";") + 1;
+ gchar *filename;
+ glong filename_length;
+
+ filename_length =
+ g_utf8_strlen (image_uri, -1) -
+ g_utf8_strlen (base64_data, -1) - 1;
+ filename = g_strndup (image_uri, filename_length);
+
+ replace_base64_image_src (
+ selection, image, base64_data, filename, "");
+ g_free (filename);
+ }
+ } else
+ image_load_and_insert_async (selection, image, image_uri);
+}
+
+/**
+ * e_html_editor_selection_clear_caret_position_marker:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Removes previously set caret position marker from composer.
+ */
+void
+e_html_editor_selection_clear_caret_position_marker (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ element = webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position");
+
+ if (element)
+ remove_node (WEBKIT_DOM_NODE (element));
+
+ g_object_unref (view);
+}
+
+WebKitDOMNode *
+e_html_editor_selection_get_caret_position_node (WebKitDOMDocument *document)
+{
+ WebKitDOMElement *element;
+
+ element = webkit_dom_document_create_element (document, "SPAN", NULL);
+ webkit_dom_element_set_id (element, "-x-evo-caret-position");
+ webkit_dom_element_set_attribute (
+ element, "style", "color: red", NULL);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element), "*", NULL);
+
+ return WEBKIT_DOM_NODE (element);
+}
+
+/**
+ * e_html_editor_selection_save_caret_position:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Saves current caret position in composer.
+ *
+ * Returns: #WebKitDOMElement that was created on caret position
+ */
+WebKitDOMElement *
+e_html_editor_selection_save_caret_position (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMNode *split_node;
+ WebKitDOMNode *start_offset_node;
+ WebKitDOMNode *caret_node;
+ WebKitDOMRange *range;
+ gulong start_offset;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_val_if_fail (view != NULL, NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+
+ e_html_editor_selection_clear_caret_position_marker (selection);
+
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return NULL;
+
+ start_offset = webkit_dom_range_get_start_offset (range, NULL);
+ start_offset_node = webkit_dom_range_get_end_container (range, NULL);
+
+ caret_node = e_html_editor_selection_get_caret_position_node (document);
+
+ if (WEBKIT_DOM_IS_TEXT (start_offset_node) && start_offset != 0) {
+ WebKitDOMText *split_text;
+
+ split_text = webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (start_offset_node),
+ start_offset, NULL);
+ split_node = WEBKIT_DOM_NODE (split_text);
+ } else {
+ split_node = start_offset_node;
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (start_offset_node),
+ caret_node,
+ split_node,
+ NULL);
+
+ return WEBKIT_DOM_ELEMENT (caret_node);
+}
+
+static void
+fix_quoting_nodes_after_caret_restoration (WebKitDOMDOMSelection *window_selection,
+ WebKitDOMNode *prev_sibling,
+ WebKitDOMNode *next_sibling)
+{
+ WebKitDOMNode *tmp_node;
+
+ if (!element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper"))
+ return;
+
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "forward", "character");
+ tmp_node = webkit_dom_node_get_next_sibling (
+ webkit_dom_node_get_first_child (prev_sibling));
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (prev_sibling),
+ tmp_node,
+ next_sibling,
+ NULL);
+
+ tmp_node = webkit_dom_node_get_first_child (prev_sibling);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (prev_sibling),
+ tmp_node,
+ webkit_dom_node_get_previous_sibling (next_sibling),
+ NULL);
+
+ remove_node (prev_sibling);
+
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "backward", "character");
+}
+
+/**
+ * e_html_editor_selection_restore_caret_position:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Restores previously saved caret position in composer.
+ */
+void
+e_html_editor_selection_restore_caret_position (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+ gboolean fix_after_quoting;
+ gboolean swap_direction = FALSE;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+
+ element = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-caret-position");
+ fix_after_quoting = element_has_class (element, "-x-evo-caret-quoting");
+
+ if (element) {
+ WebKitDOMDOMWindow *window;
+ WebKitDOMNode *parent_node;
+ WebKitDOMDOMSelection *window_selection;
+ WebKitDOMNode *prev_sibling;
+ WebKitDOMNode *next_sibling;
+
+ if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)))
+ swap_direction = TRUE;
+
+ window = webkit_dom_document_get_default_view (document);
+ window_selection = webkit_dom_dom_window_get_selection (window);
+ parent_node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+ /* If parent is BODY element, we try to restore the position on the
+ * element that is next to us */
+ if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) {
+ /* Look if we have DIV on right */
+ next_sibling = webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (element));
+ if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) {
+ e_html_editor_selection_clear_caret_position_marker (selection);
+ return;
+ }
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-paragraph")) {
+ remove_node (WEBKIT_DOM_NODE (element));
+
+ move_caret_into_element (
+ document, WEBKIT_DOM_ELEMENT (next_sibling));
+
+ goto out;
+ }
+ }
+
+ move_caret_into_element (document, element);
+
+ if (fix_after_quoting) {
+ prev_sibling = webkit_dom_node_get_previous_sibling (
+ WEBKIT_DOM_NODE (element));
+ next_sibling = webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (element));
+ if (!next_sibling)
+ fix_after_quoting = FALSE;
+ }
+
+ remove_node (WEBKIT_DOM_NODE (element));
+
+ if (fix_after_quoting)
+ fix_quoting_nodes_after_caret_restoration (
+ window_selection, prev_sibling, next_sibling);
+ out:
+ /* FIXME If caret position is restored and afterwards the
+ * position is saved it is not on the place where it supposed
+ * to be (it is in the beginning of parent's element. It can
+ * be avoided by moving with the caret. */
+ if (swap_direction) {
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "forward", "character");
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "backward", "character");
+ } else {
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "backward", "character");
+ webkit_dom_dom_selection_modify (
+ window_selection, "move", "forward", "character");
+ }
+ }
+}
+
+static gint
+find_where_to_break_line (WebKitDOMNode *node,
+ gint max_len,
+ gint word_wrap_length)
+{
+ gchar *str, *text_start;
+ gunichar uc;
+ gint pos;
+ gint last_space = 0;
+ gint length;
+ gint ret_val = 0;
+ gchar* position;
+
+ text_start = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
+ length = g_utf8_strlen (text_start, -1);
+
+ pos = 1;
+ last_space = 0;
+ str = text_start;
+ do {
+ uc = g_utf8_get_char (str);
+ if (!uc) {
+ g_free (text_start);
+ if (pos <= max_len)
+ return pos;
+ else
+ return last_space;
+ }
+
+ /* If last_space is zero then the word is longer than
+ * word_wrap_length characters, so continue until we find
+ * a space */
+ if ((pos > max_len) && (last_space > 0)) {
+ if (last_space > word_wrap_length) {
+ g_free (text_start);
+ return last_space;
+ }
+
+ if (last_space > max_len) {
+ if (g_unichar_isspace (g_utf8_get_char (text_start)))
+ ret_val = 1;
+
+ g_free (text_start);
+ return ret_val;
+ }
+
+ if (last_space == max_len - 1) {
+ uc = g_utf8_get_char (str);
+ if (g_unichar_isspace (uc))
+ last_space++;
+ }
+
+ g_free (text_start);
+ return last_space;
+ }
+
+ if (g_unichar_isspace (uc))
+ last_space = pos;
+
+ pos += 1;
+ str = g_utf8_next_char (str);
+ } while (*str);
+
+ position = g_utf8_offset_to_pointer (text_start, max_len);
+
+ if (g_unichar_isspace (g_utf8_get_char (position))) {
+ ret_val = max_len + 1;
+ } else {
+ if (last_space < max_len) {
+ ret_val = last_space;
+ } else {
+ if (length > word_wrap_length)
+ ret_val = last_space;
+ else
+ ret_val = 0;
+ }
+ }
+
+ g_free (text_start);
+
+ return ret_val;
+}
+
+static WebKitDOMElement *
+wrap_lines (EHTMLEditorSelection *selection,
+ WebKitDOMNode *paragraph,
+ WebKitDOMDocument *document,
+ gboolean remove_all_br,
+ gint word_wrap_length)
+{
+ WebKitDOMNode *node, *start_node;
+ WebKitDOMNode *paragraph_clone;
+ WebKitDOMDocumentFragment *fragment;
+ WebKitDOMElement *element;
+ WebKitDOMNodeList *wrap_br;
+ gint len, ii, br_count;
+ gulong length_left;
+ glong paragraph_char_count;
+ gchar *text_content;
+
+ if (selection) {
+ paragraph_char_count = g_utf8_strlen (
+ e_html_editor_selection_get_string (selection), -1);
+
+ fragment = webkit_dom_range_clone_contents (
+ html_editor_selection_get_current_range (selection), NULL);
+
+ /* Select all BR elements or just ours that are used for wrapping.
+ * We are not removing user BR elements when this function is activated
+ * from Format->Wrap Lines action */
+ wrap_br = webkit_dom_document_fragment_query_selector_all (
+ fragment,
+ remove_all_br ? "br" : "br.-x-evo-wrap-br",
+ NULL);
+ } else {
+ WebKitDOMElement *caret_node;
+
+ if (!webkit_dom_node_has_child_nodes (paragraph))
+ return WEBKIT_DOM_ELEMENT (paragraph);
+
+ paragraph_clone = webkit_dom_node_clone_node (paragraph, TRUE);
+ caret_node = webkit_dom_element_query_selector (
+ WEBKIT_DOM_ELEMENT (paragraph_clone),
+ "span#-x-evo-caret-position", NULL);
+ text_content = webkit_dom_node_get_text_content (paragraph_clone);
+ paragraph_char_count = g_utf8_strlen (text_content, -1);
+ if (caret_node)
+ paragraph_char_count--;
+ g_free (text_content);
+
+ wrap_br = webkit_dom_element_query_selector_all (
+ WEBKIT_DOM_ELEMENT (paragraph_clone),
+ remove_all_br ? "br" : "br.-x-evo-wrap-br",
+ NULL);
+ }
+
+ /* And remove them */
+ br_count = webkit_dom_node_list_get_length (wrap_br);
+ for (ii = 0; ii < br_count; ii++)
+ remove_node (webkit_dom_node_list_item (wrap_br, ii));
+
+ if (selection)
+ node = WEBKIT_DOM_NODE (fragment);
+ else {
+ webkit_dom_node_normalize (paragraph_clone);
+ node = webkit_dom_node_get_first_child (paragraph_clone);
+ if (node) {
+ text_content = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 ("\n", text_content) == 0)
+ node = webkit_dom_node_get_next_sibling (node);
+ g_free (text_content);
+ }
+ }
+
+ start_node = node;
+ len = 0;
+ while (node) {
+ gint offset = 0;
+
+ if (WEBKIT_DOM_IS_TEXT (node)) {
+ const gchar *newline;
+ WebKitDOMNode *next_sibling;
+
+ /* If there is temporary hidden space we remove it */
+ text_content = webkit_dom_node_get_text_content (node);
+ if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+ if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ 0,
+ 1,
+ NULL);
+ if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ g_utf8_strlen (text_content, -1) - 1,
+ 1,
+ NULL);
+ g_free (text_content);
+ text_content = webkit_dom_node_get_text_content (node);
+ }
+ newline = g_strstr_len (text_content, -1, "\n");
+
+ next_sibling = node;
+ while (newline) {
+ WebKitDOMElement *element;
+
+ next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (next_sibling),
+ g_utf8_pointer_to_offset (text_content, newline),
+ NULL));
+
+ if (!next_sibling)
+ break;
+
+ element = webkit_dom_document_create_element (
+ document, "BR", NULL);
+ element_add_class (element, "-x-evo-temp-wrap-text-br");
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (next_sibling),
+ WEBKIT_DOM_NODE (element),
+ next_sibling,
+ NULL);
+
+ g_free (text_content);
+
+ text_content = webkit_dom_node_get_text_content (next_sibling);
+ if (g_str_has_prefix (text_content, "\n")) {
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL);
+ g_free (text_content);
+ text_content =
+ webkit_dom_node_get_text_content (next_sibling);
+ }
+ newline = g_strstr_len (text_content, -1, "\n");
+ }
+ g_free (text_content);
+ } else {
+ /* If element is ANCHOR we wrap it separately */
+ if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+ glong anchor_length;
+
+ text_content = webkit_dom_node_get_text_content (node);
+ anchor_length = g_utf8_strlen (text_content, -1);
+ if (len + anchor_length > word_wrap_length) {
+ element = webkit_dom_document_create_element (
+ document, "BR", NULL);
+ element_add_class (element, "-x-evo-wrap-br");
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ node,
+ NULL);
+ len = anchor_length;
+ } else
+ len += anchor_length;
+
+ g_free (text_content);
+ node = webkit_dom_node_get_next_sibling (node);
+ continue;
+ }
+
+ if (is_caret_position_node (node)) {
+ node = webkit_dom_node_get_next_sibling (node);
+ continue;
+ }
+
+ /* When we are not removing user-entered BR elements (lines wrapped by user),
+ * we need to skip those elements */
+ if (!remove_all_br && WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) {
+ if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+ len = 0;
+ node = webkit_dom_node_get_next_sibling (node);
+ continue;
+ }
+ }
+ goto next_node;
+ }
+
+ /* If length of this node + what we already have is still less
+ * then word_wrap_length characters, then just join it and continue to next
+ * node */
+ length_left = webkit_dom_character_data_get_length (
+ WEBKIT_DOM_CHARACTER_DATA (node));
+
+ if ((length_left + len) < word_wrap_length) {
+ len += length_left;
+ goto next_node;
+ }
+
+ /* wrap until we have something */
+ while ((length_left + len) > word_wrap_length) {
+ /* Find where we can line-break the node so that it
+ * effectively fills the rest of current row */
+ offset = find_where_to_break_line (
+ node, word_wrap_length - len, word_wrap_length);
+
+ element = webkit_dom_document_create_element (document, "BR", NULL);
+ element_add_class (element, "-x-evo-wrap-br");
+
+ if (offset > 0 && offset <= word_wrap_length) {
+ if (offset != length_left)
+ webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (node), offset, NULL);
+
+ if (webkit_dom_node_get_next_sibling (node)) {
+ gchar *nd_content;
+ WebKitDOMNode *nd = webkit_dom_node_get_next_sibling (node);
+
+ nd = webkit_dom_node_get_next_sibling (node);
+ nd_content = webkit_dom_node_get_text_content (nd);
+ if (nd_content && *nd_content) {
+ if (g_str_has_prefix (nd_content, " "))
+ webkit_dom_character_data_replace_data (
+ WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL);
+ g_free (nd_content);
+ nd_content = webkit_dom_node_get_text_content (nd);
+ if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
+ remove_node (nd);
+ g_free (nd_content);
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ nd,
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ } else if (offset > word_wrap_length) {
+ if (offset != length_left)
+ webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (node), offset + 1, NULL);
+
+ if (webkit_dom_node_get_next_sibling (node)) {
+ gchar *nd_content;
+ WebKitDOMNode *nd = webkit_dom_node_get_next_sibling (node);
+
+ nd = webkit_dom_node_get_next_sibling (node);
+ nd_content = webkit_dom_node_get_text_content (nd);
+ if (nd_content && *nd_content) {
+ if (g_str_has_prefix (nd_content, " "))
+ webkit_dom_character_data_replace_data (
+ WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL);
+ g_free (nd_content);
+ nd_content = webkit_dom_node_get_text_content (nd);
+ if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
+ remove_node (nd);
+ g_free (nd_content);
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ nd,
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ }
+ len = 0;
+ break;
+ } else {
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ node,
+ NULL);
+ }
+ length_left = webkit_dom_character_data_get_length (
+ WEBKIT_DOM_CHARACTER_DATA (node));
+
+ len = 0;
+ }
+ len += length_left - offset;
+ next_node:
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
+ len = 0;
+
+ /* Move to next node */
+ if (webkit_dom_node_has_child_nodes (node)) {
+ node = webkit_dom_node_get_first_child (node);
+ } else if (webkit_dom_node_get_next_sibling (node)) {
+ node = webkit_dom_node_get_next_sibling (node);
+ } else {
+ if (webkit_dom_node_is_equal_node (node, start_node))
+ break;
+
+ node = webkit_dom_node_get_parent_node (node);
+ if (node)
+ node = webkit_dom_node_get_next_sibling (node);
+ }
+ }
+
+ if (selection) {
+ gchar *html;
+
+ /* Create a wrapper DIV and put the processed content into it */
+ element = webkit_dom_document_create_element (document, "DIV", NULL);
+ element_add_class (element, "-x-evo-paragraph");
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element),
+ WEBKIT_DOM_NODE (start_node),
+ NULL);
+
+ webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+ /* Get HTML code of the processed content */
+ html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (element));
+
+ /* Overwrite the current selection be the processed content */
+ e_html_editor_selection_insert_html (selection, html);
+
+ g_free (html);
+
+ return NULL;
+ } else {
+ webkit_dom_node_normalize (paragraph_clone);
+
+ /* Replace paragraph with wrapped one */
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (paragraph),
+ paragraph_clone,
+ paragraph,
+ NULL);
+
+ return WEBKIT_DOM_ELEMENT (paragraph_clone);
+ }
+}
+
+void
+e_html_editor_selection_set_indented_style (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ gint width)
+{
+ EHTMLEditorSelectionAlignment alignment;
+ gchar *style;
+ const gchar *align_value;
+ gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
+ gint start = 0, end = 0;
+
+ alignment = e_html_editor_selection_get_alignment (selection);
+ align_value = get_css_alignment_value (alignment);
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+ start = SPACES_PER_INDENTATION;
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+ start = SPACES_PER_INDENTATION;
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+ start = 0;
+ end = SPACES_PER_INDENTATION;
+ }
+
+ webkit_dom_element_set_class_name (element, "-x-evo-indented");
+ /* We don't want vertical space bellow and above blockquote inserted by
+ * WebKit's User Agent Stylesheet. We have to override it through style attribute. */
+ if (is_in_html_mode (selection))
+ style = g_strdup_printf (
+ "-webkit-margin-start: %dch; -webkit-margin-end : %dch; %s",
+ start, end, align_value);
+ else
+ style = g_strdup_printf (
+ "-webkit-margin-start: %dch; -webkit-margin-end : %dch; "
+ "word-wrap: normal; width: %dch; %s",
+ start, end, word_wrap_length, align_value);
+
+ webkit_dom_element_set_attribute (element, "style", style, NULL);
+ g_free (style);
+}
+
+WebKitDOMElement *
+e_html_editor_selection_get_indented_element (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ gint width)
+{
+ WebKitDOMElement *element;
+
+ element = webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL);
+ e_html_editor_selection_set_indented_style (selection, element, width);
+
+ return element;
+}
+
+void
+e_html_editor_selection_set_paragraph_style (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ gint width,
+ gint offset,
+ const gchar *style_to_add)
+{
+ EHTMLEditorSelectionAlignment alignment;
+ const gchar *align_value = NULL;
+ char *style = NULL;
+ gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
+
+ alignment = e_html_editor_selection_get_alignment (selection);
+ align_value = get_css_alignment_value (alignment);
+
+ element_add_class (element, "-x-evo-paragraph");
+ if (!is_in_html_mode (selection)) {
+ style = g_strdup_printf (
+ "width: %dch; word-wrap: normal; %s %s",
+ (word_wrap_length + offset), align_value, style_to_add);
+ } else {
+ if (*align_value || *style_to_add)
+ style = g_strdup_printf (
+ "%s %s", align_value, style_to_add);
+ }
+ if (style) {
+ webkit_dom_element_set_attribute (element, "style", style, NULL);
+ g_free (style);
+ }
+}
+
+WebKitDOMElement *
+e_html_editor_selection_get_paragraph_element (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ gint width,
+ gint offset)
+{
+ WebKitDOMElement *element;
+
+ element = webkit_dom_document_create_element (document, "DIV", NULL);
+ e_html_editor_selection_set_paragraph_style (selection, element, width, offset, "");
+
+ return element;
+}
+
+WebKitDOMElement *
+e_html_editor_selection_put_node_into_paragraph (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ WebKitDOMNode *node,
+ WebKitDOMNode *caret_position)
+{
+ WebKitDOMRange *range;
+ WebKitDOMElement *container;
+
+ range = webkit_dom_document_create_range (document);
+ container = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0);
+ webkit_dom_range_select_node (range, node, NULL);
+ webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL);
+ /* We have to move caret position inside this container */
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (container), caret_position, NULL);
+
+ return container;
+}
+
+/**
+ * e_html_editor_selection_wrap_lines:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Wraps all lines in current selection to be 71 characters long.
+ */
+void
+e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitDOMRange *range;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *active_paragraph, *caret;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ g_object_unref (view);
+
+ caret = e_html_editor_selection_save_caret_position (selection);
+ if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") == 0) {
+ WebKitDOMNode *end_container;
+ WebKitDOMNode *parent;
+ WebKitDOMNode *paragraph;
+ gchar *text_content;
+
+ /* We need to save caret position and restore it after
+ * wrapping the selection, but we need to save it before we
+ * start to modify selection */
+ range = html_editor_selection_get_current_range (selection);
+ if (!range)
+ return;
+
+ end_container = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+ /* Wrap only text surrounded in DIV and P tags */
+ parent = webkit_dom_node_get_parent_node(end_container);
+ if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) ||
+ WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent)) {
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph");
+ paragraph = parent;
+ } else {
+ WebKitDOMElement *parent_div =
+ e_html_editor_dom_node_find_parent_element (parent, "DIV");
+
+ if (element_has_class (parent_div, "-x-evo-paragraph")) {
+ paragraph = WEBKIT_DOM_NODE (parent_div);
+ } else {
+ if (!caret)
+ return;
+
+ /* We try to select previous sibling */
+ paragraph = webkit_dom_node_get_previous_sibling (
+ WEBKIT_DOM_NODE (caret));
+ if (paragraph) {
+ /* When there is just text without container
+ * we have to surround it with paragraph div */
+ if (WEBKIT_DOM_IS_TEXT (paragraph))
+ paragraph = WEBKIT_DOM_NODE (
+ e_html_editor_selection_put_node_into_paragraph (
+ selection, document, paragraph,
+ WEBKIT_DOM_NODE (caret)));
+ } else {
+ /* When some weird element is selected, return */
+ e_html_editor_selection_clear_caret_position_marker (selection);
+ return;
+ }
+ }
+ }
+
+ if (!paragraph)
+ return;
+
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (paragraph), "style");
+ webkit_dom_element_set_id (
+ WEBKIT_DOM_ELEMENT (paragraph), "-x-evo-active-paragraph");
+
+ text_content = webkit_dom_node_get_text_content (paragraph);
+ /* If there is hidden space character in the beginning we remove it */
+ if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+ if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_node_get_first_child (paragraph);
+
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ 0,
+ 1,
+ NULL);
+ }
+ if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_node_get_last_child (paragraph);
+
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ g_utf8_strlen (text_content, -1) -1,
+ 1,
+ NULL);
+ }
+ }
+ g_free (text_content);
+
+ wrap_lines (
+ NULL, paragraph, document, FALSE,
+ selection->priv->word_wrap_length);
+
+ } else {
+ e_html_editor_selection_save_caret_position (selection);
+ /* If we have selection -> wrap it */
+ wrap_lines (
+ selection, NULL, document, FALSE,
+ selection->priv->word_wrap_length);
+ }
+
+ active_paragraph = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-active-paragraph");
+ /* We have to move caret on position where it was before modifying the text */
+ e_html_editor_selection_restore_caret_position (selection);
+
+ /* Set paragraph as non-active */
+ if (active_paragraph)
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (active_paragraph), "id");
+}
+
+WebKitDOMElement *
+e_html_editor_selection_wrap_paragraph_length (EHTMLEditorSelection *selection,
+ WebKitDOMElement *paragraph,
+ gint length)
+{
+ WebKitDOMDocument *document;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+ g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+ g_return_val_if_fail (length > 10, NULL);
+
+ document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (paragraph));
+
+ return wrap_lines (
+ NULL, WEBKIT_DOM_NODE (paragraph), document, FALSE, length);
+}
+
+void
+e_html_editor_selection_wrap_paragraphs_in_document (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document)
+{
+ WebKitDOMNodeList *list;
+ gint ii, length;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ list = webkit_dom_document_query_selector_all (
+ document, "div.-x-evo-paragraph:not(#-x-evo-input-start)", NULL);
+
+ length = webkit_dom_node_list_get_length (list);
+
+ for (ii = 0; ii < length; ii++) {
+ gint quote, citation_level;
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ citation_level = get_citation_level (node);
+ quote = citation_level ? citation_level + 1 : 0;
+
+ if (node_is_list (node)) {
+ WebKitDOMNode *item = webkit_dom_node_get_first_child (node);
+
+ while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+ e_html_editor_selection_wrap_paragraph_length (
+ selection,
+ WEBKIT_DOM_ELEMENT (item),
+ selection->priv->word_wrap_length - quote);
+ item = webkit_dom_node_get_next_sibling (item);
+ }
+ } else {
+ e_html_editor_selection_wrap_paragraph_length (
+ selection,
+ WEBKIT_DOM_ELEMENT (node),
+ selection->priv->word_wrap_length - quote);
+ }
+ }
+}
+
+WebKitDOMElement *
+e_html_editor_selection_wrap_paragraph (EHTMLEditorSelection *selection,
+ WebKitDOMElement *paragraph)
+{
+ gint indentation_level, citation_level, quote;
+ gint final_width, word_wrap_length, offset = 0;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+ g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+
+ word_wrap_length = selection->priv->word_wrap_length;
+ indentation_level = get_indentation_level (paragraph);
+ citation_level = get_citation_level (WEBKIT_DOM_NODE (paragraph));
+
+ if (node_is_list (WEBKIT_DOM_NODE (paragraph)) ||
+ WEBKIT_DOM_IS_HTMLLI_ELEMENT (paragraph)) {
+
+ gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph));
+ indentation_level = 0;
+
+ if (list_level > 0)
+ offset = list_level * -SPACES_PER_LIST_LEVEL;
+ else
+ offset = -SPACES_PER_LIST_LEVEL;
+ }
+
+ quote = citation_level ? citation_level + 1 : 0;
+ quote *= 2;
+
+ final_width = word_wrap_length - quote + offset;
+ final_width -= SPACES_PER_INDENTATION * indentation_level;
+
+ return e_html_editor_selection_wrap_paragraph_length (
+ selection, WEBKIT_DOM_ELEMENT (paragraph), final_width);
+}
+
+/**
+ * e_html_editor_selection_save:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Saves current cursor position or current selection range. The selection can
+ * be later restored by calling e_html_editor_selection_restore().
+ *
+ * Note that calling e_html_editor_selection_save() overwrites previously saved
+ * position.
+ *
+ * Note that this method inserts special markings into the HTML code that are
+ * used to later restore the selection. It can happen that by deleting some
+ * segments of the document some of the markings are deleted too. In that case
+ * restoring the selection by e_html_editor_selection_restore() can fail. Also by
+ * moving text segments (Cut & Paste) can result in moving the markings
+ * elsewhere, thus e_html_editor_selection_restore() will restore the selection
+ * incorrectly.
+ *
+ * It is recommended to use this method only when you are not planning to make
+ * bigger changes to content or structure of the document (formatting changes
+ * are usually OK).
+ */
+void
+e_html_editor_selection_save (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ WebKitDOMNode *container;
+ WebKitDOMElement *marker;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ document = webkit_web_view_get_dom_document (web_view);
+
+ /* First remove all markers (if present) */
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-start-marker");
+ if (marker != NULL)
+ remove_node (WEBKIT_DOM_NODE (marker));
+
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-end-marker");
+ if (marker != NULL)
+ remove_node (WEBKIT_DOM_NODE (marker));
+
+ range = html_editor_selection_get_current_range (selection);
+
+ if (range != NULL) {
+ WebKitDOMNode *marker_node;
+ WebKitDOMNode *parent_node;
+ WebKitDOMNode *split_node;
+ glong offset;
+
+ marker = webkit_dom_document_create_element (
+ document, "SPAN", NULL);
+ webkit_dom_element_set_id (
+ marker, "-x-evo-selection-start-marker");
+
+ container = webkit_dom_range_get_start_container (range, NULL);
+ offset = webkit_dom_range_get_start_offset (range, NULL);
+
+ if (WEBKIT_DOM_IS_TEXT (container)) {
+ WebKitDOMText *split_text;
+
+ split_text = webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (container), offset, NULL);
+ split_node = WEBKIT_DOM_NODE (split_text);
+ } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
+ webkit_dom_node_insert_before (
+ container,
+ WEBKIT_DOM_NODE (marker),
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (container)),
+ NULL);
+
+ goto end_marker;
+ } else {
+ if (!webkit_dom_node_get_previous_sibling (container)) {
+ split_node = webkit_dom_node_get_parent_node (
+ container);
+ } else if (!webkit_dom_node_get_next_sibling (container)) {
+ split_node = webkit_dom_node_get_parent_node (
+ container);
+ split_node = webkit_dom_node_get_next_sibling (
+ split_node);
+ } else
+ split_node = container;
+ }
+
+ if (!split_node) {
+ webkit_dom_node_insert_before (
+ container,
+ WEBKIT_DOM_NODE (marker),
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (container)),
+ NULL);
+ } else {
+ marker_node = WEBKIT_DOM_NODE (marker);
+ parent_node = webkit_dom_node_get_parent_node (split_node);
+
+ webkit_dom_node_insert_before (
+ parent_node, marker_node, split_node, NULL);
+ }
+ end_marker:
+ marker = webkit_dom_document_create_element (
+ document, "SPAN", NULL);
+ webkit_dom_element_set_id (
+ marker, "-x-evo-selection-end-marker");
+
+ container = webkit_dom_range_get_end_container (range, NULL);
+ offset = webkit_dom_range_get_end_offset (range, NULL);
+
+ if (WEBKIT_DOM_IS_TEXT (container) && offset != 0) {
+ WebKitDOMText *split_text;
+
+ split_text = webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (container), offset, NULL);
+ split_node = WEBKIT_DOM_NODE (split_text);
+ } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
+ webkit_dom_node_append_child (
+ container, WEBKIT_DOM_NODE (marker), NULL);
+ g_object_unref (view);
+ return;
+ } else {
+ if (!webkit_dom_node_get_previous_sibling (container)) {
+ split_node = webkit_dom_node_get_parent_node (
+ container);
+ } else if (!webkit_dom_node_get_next_sibling (container)) {
+ split_node = webkit_dom_node_get_parent_node (
+ container);
+ split_node = webkit_dom_node_get_next_sibling (
+ split_node);
+ } else
+ split_node = container;
+ }
+
+ marker_node = WEBKIT_DOM_NODE (marker);
+
+ if (split_node) {
+ parent_node = webkit_dom_node_get_parent_node (split_node);
+
+ webkit_dom_node_insert_before (
+ parent_node, marker_node, split_node, NULL);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (container),
+ marker_node,
+ NULL);
+ }
+ }
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_restore:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Restores cursor position or selection range that was saved by
+ * e_html_editor_selection_save().
+ *
+ * Note that calling this function without calling e_html_editor_selection_save()
+ * before is a programming error and the behavior is undefined.
+ */
+void
+e_html_editor_selection_restore (EHTMLEditorSelection *selection)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMRange *range;
+ WebKitDOMElement *marker;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ document = webkit_web_view_get_dom_document (web_view);
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+ range = webkit_dom_document_create_range (document);
+
+ if (range != NULL) {
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-start-marker");
+ if (!marker) {
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-end-marker");
+ if (marker)
+ remove_node (WEBKIT_DOM_NODE (marker));
+ return;
+ }
+
+ webkit_dom_range_set_start_after (
+ range, WEBKIT_DOM_NODE (marker), NULL);
+ remove_node (WEBKIT_DOM_NODE (marker));
+
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-end-marker");
+ if (!marker) {
+ marker = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-selection-start-marker");
+ if (marker)
+ remove_node (WEBKIT_DOM_NODE (marker));
+ return;
+ }
+
+ webkit_dom_range_set_end_before (
+ range, WEBKIT_DOM_NODE (marker), NULL);
+ remove_node (WEBKIT_DOM_NODE (marker));
+
+ webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+ webkit_dom_dom_selection_add_range (dom_selection, range);
+ }
+
+ g_object_unref (view);
+}
+
+static void
+html_editor_selection_modify (EHTMLEditorSelection *selection,
+ const gchar *alter,
+ gboolean forward,
+ EHTMLEditorSelectionGranularity granularity)
+{
+ EHTMLEditorView *view;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ const gchar *granularity_str;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+ view = e_html_editor_selection_ref_html_editor_view (selection);
+ g_return_if_fail (view != NULL);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ document = webkit_web_view_get_dom_document (web_view);
+ window = webkit_dom_document_get_default_view (document);
+ dom_selection = webkit_dom_dom_window_get_selection (window);
+
+ switch (granularity) {
+ case E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER:
+ granularity_str = "character";
+ break;
+ case E_HTML_EDITOR_SELECTION_GRANULARITY_WORD:
+ granularity_str = "word";
+ break;
+ }
+
+ webkit_dom_dom_selection_modify (
+ dom_selection, alter,
+ forward ? "forward" : "backward",
+ granularity_str);
+
+ g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_extend:
+ * @selection: an #EHTMLEditorSelection
+ * @forward: whether to extend selection forward or backward
+ * @granularity: granularity of the extension
+ *
+ * Extends current selection in given direction by given granularity.
+ */
+void
+e_html_editor_selection_extend (EHTMLEditorSelection *selection,
+ gboolean forward,
+ EHTMLEditorSelectionGranularity granularity)
+{
+ html_editor_selection_modify (selection, "extend", forward, granularity);
+}
+
+/**
+ * e_html_editor_selection_move:
+ * @selection: an #EHTMLEditorSelection
+ * @forward: whether to move the selection forward or backward
+ * @granularity: granularity of the movement
+ *
+ * Moves current selection in given direction by given granularity
+ */
+void
+e_html_editor_selection_move (EHTMLEditorSelection *selection,
+ gboolean forward,
+ EHTMLEditorSelectionGranularity granularity)
+{
+ html_editor_selection_modify (selection, "move", forward, granularity);
+}
+
+void
+e_html_editor_selection_scroll_to_caret (EHTMLEditorSelection *selection)
+{
+ WebKitDOMElement *caret;
+
+ caret = e_html_editor_selection_save_caret_position (selection);
+
+ webkit_dom_element_scroll_into_view (caret, TRUE);
+
+ e_html_editor_selection_clear_caret_position_marker (selection);
+}
diff --git a/e-util/e-html-editor-selection.h b/e-util/e-html-editor-selection.h
new file mode 100644
index 0000000000..c0d2d1817f
--- /dev/null
+++ b/e-util/e-html-editor-selection.h
@@ -0,0 +1,250 @@
+/*
+ * e-html-editor-selection.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_SELECTION_H
+#define E_HTML_EDITOR_SELECTION_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-util-enums.h>
+#include <webkit/webkit.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_SELECTION \
+ (e_html_editor_selection_get_type ())
+#define E_HTML_EDITOR_SELECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelection))
+#define E_HTML_EDITOR_SELECTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass))
+#define E_IS_HTML_EDITOR_SELECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_SELECTION))
+#define E_IS_HTML_EDITOR_SELECTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_SELECTION))
+#define E_HTML_EDITOR_SELECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass))
+
+G_BEGIN_DECLS
+
+struct _EHTMLEditorView;
+
+typedef struct _EHTMLEditorSelection EHTMLEditorSelection;
+typedef struct _EHTMLEditorSelectionClass EHTMLEditorSelectionClass;
+typedef struct _EHTMLEditorSelectionPrivate EHTMLEditorSelectionPrivate;
+
+struct _EHTMLEditorSelection {
+ GObject parent;
+ EHTMLEditorSelectionPrivate *priv;
+};
+
+struct _EHTMLEditorSelectionClass {
+ GObjectClass parent_class;
+};
+
+GType e_html_editor_selection_get_type
+ (void) G_GNUC_CONST;
+struct _EHTMLEditorView *
+ e_html_editor_selection_ref_html_editor_view
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_block_selection_changed
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_unblock_selection_changed
+ (EHTMLEditorSelection *selection);
+gint e_html_editor_selection_get_word_wrap_length
+ (EHTMLEditorSelection *selection);
+gboolean e_html_editor_selection_has_text
+ (EHTMLEditorSelection *selection);
+gchar * e_html_editor_selection_get_caret_word
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_replace_caret_word
+ (EHTMLEditorSelection *selection,
+ const gchar *replacement);
+EHTMLEditorSelectionAlignment
+ e_html_editor_selection_get_alignment
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_alignment
+ (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionAlignment alignment);
+const gchar * e_html_editor_selection_get_background_color
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_background_color
+ (EHTMLEditorSelection *selection,
+ const gchar *color);
+void e_html_editor_selection_get_font_color
+ (EHTMLEditorSelection *selection,
+ GdkRGBA *rgba);
+void e_html_editor_selection_set_font_color
+ (EHTMLEditorSelection *selection,
+ const GdkRGBA *rgba);
+const gchar * e_html_editor_selection_get_font_name
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_font_name
+ (EHTMLEditorSelection *selection,
+ const gchar *font_name);
+guint e_html_editor_selection_get_font_size
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_font_size
+ (EHTMLEditorSelection *selection,
+ guint font_size);
+EHTMLEditorSelectionBlockFormat
+ e_html_editor_selection_get_block_format
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_block_format
+ (EHTMLEditorSelection *selection,
+ EHTMLEditorSelectionBlockFormat format);
+gboolean e_html_editor_selection_is_citation
+ (EHTMLEditorSelection *selection);
+gboolean e_html_editor_selection_is_indented
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_indent (EHTMLEditorSelection *selection);
+void e_html_editor_selection_unindent
+ (EHTMLEditorSelection *selection);
+gboolean e_html_editor_selection_is_bold (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_bold
+ (EHTMLEditorSelection *selection,
+ gboolean bold);
+gboolean e_html_editor_selection_is_italic
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_italic
+ (EHTMLEditorSelection *selection,
+ gboolean italic);
+gboolean e_html_editor_selection_is_monospaced
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_monospaced
+ (EHTMLEditorSelection *selection,
+ gboolean monospaced);
+gboolean e_html_editor_selection_is_strikethrough
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_strikethrough
+ (EHTMLEditorSelection *selection,
+ gboolean strikethrough);
+gboolean e_html_editor_selection_is_superscript
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_superscript
+ (EHTMLEditorSelection *selection,
+ gboolean superscript);
+gboolean e_html_editor_selection_is_subscript
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_subscript
+ (EHTMLEditorSelection *selection,
+ gboolean subscript);
+gboolean e_html_editor_selection_is_underline
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_underline
+ (EHTMLEditorSelection *selection,
+ gboolean underline);
+void e_html_editor_selection_unlink (EHTMLEditorSelection *selection);
+void e_html_editor_selection_create_link
+ (EHTMLEditorSelection *selection,
+ const gchar *uri);
+const gchar * e_html_editor_selection_get_string
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_replace (EHTMLEditorSelection *selection,
+ const gchar *new_string);
+void e_html_editor_selection_insert_html
+ (EHTMLEditorSelection *selection,
+ const gchar *html_text);
+void e_html_editor_selection_replace_image_src
+ (EHTMLEditorSelection *selection,
+ WebKitDOMElement *image,
+ const gchar *image_uri);
+void e_html_editor_selection_insert_image
+ (EHTMLEditorSelection *selection,
+ const gchar *image_uri);
+void e_html_editor_selection_insert_text
+ (EHTMLEditorSelection *selection,
+ const gchar *plain_text);
+void e_html_editor_selection_clear_caret_position_marker
+ (EHTMLEditorSelection *selection);
+WebKitDOMNode *
+ e_html_editor_selection_get_caret_position_node
+ (WebKitDOMDocument *document);
+WebKitDOMElement *
+ e_html_editor_selection_save_caret_position
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_restore_caret_position
+ (EHTMLEditorSelection *selection);
+void e_html_editor_selection_set_indented_style
+ (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ gint width);
+WebKitDOMElement *
+ e_html_editor_selection_get_indented_element
+ (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ gint width);
+void e_html_editor_selection_set_paragraph_style
+ (EHTMLEditorSelection *selection,
+ WebKitDOMElement *element,
+ gint width,
+ gint offset,
+ const gchar *style_to_add);
+WebKitDOMElement *
+ e_html_editor_selection_get_paragraph_element
+ (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ gint width,
+ gint offset);
+WebKitDOMElement *
+ e_html_editor_selection_put_node_into_paragraph
+ (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ WebKitDOMNode *node,
+ WebKitDOMNode *caret_position);
+void e_html_editor_selection_wrap_lines
+ (EHTMLEditorSelection *selection);
+WebKitDOMElement *
+ e_html_editor_selection_wrap_paragraph_length
+ (EHTMLEditorSelection *selection,
+ WebKitDOMElement *paragraph,
+ gint length);
+void e_html_editor_selection_wrap_paragraphs_in_document
+ (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document);
+WebKitDOMElement *
+ e_html_editor_selection_wrap_paragraph
+ (EHTMLEditorSelection *selection,
+ WebKitDOMElement *paragraph);
+void e_html_editor_selection_save (EHTMLEditorSelection *selection);
+void e_html_editor_selection_restore (EHTMLEditorSelection *selection);
+void e_html_editor_selection_move (EHTMLEditorSelection *selection,
+ gboolean forward,
+ EHTMLEditorSelectionGranularity granularity);
+void e_html_editor_selection_extend (EHTMLEditorSelection *selection,
+ gboolean forward,
+ EHTMLEditorSelectionGranularity granularity);
+void e_html_editor_selection_scroll_to_caret
+ (EHTMLEditorSelection *selection);
+EHTMLEditorSelectionBlockFormat
+ e_html_editor_selection_get_list_format_from_node
+ (WebKitDOMNode *node);
+EHTMLEditorSelectionAlignment
+ e_html_editor_selection_get_list_alignment_from_node
+ (WebKitDOMNode *node);
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_SELECTION_H */
diff --git a/e-util/e-html-editor-spell-check-dialog.c b/e-util/e-html-editor-spell-check-dialog.c
new file mode 100644
index 0000000000..b399dfabd8
--- /dev/null
+++ b/e-util/e-html-editor-spell-check-dialog.c
@@ -0,0 +1,710 @@
+/*
+ * e-html-editor-spell-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-spell-check-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor-view.h"
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
+
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogPrivate))
+
+struct _EHTMLEditorSpellCheckDialogPrivate {
+ GtkWidget *add_word_button;
+ GtkWidget *back_button;
+ GtkWidget *dictionary_combo;
+ GtkWidget *ignore_button;
+ GtkWidget *replace_button;
+ GtkWidget *replace_all_button;
+ GtkWidget *skip_button;
+ GtkWidget *suggestion_label;
+ GtkWidget *tree_view;
+
+ WebKitDOMDOMSelection *selection;
+
+ gchar *word;
+ ESpellDictionary *current_dict;
+};
+
+enum {
+ COLUMN_NAME,
+ COLUMN_DICTIONARY,
+ NUM_COLUMNS
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorSpellCheckDialog,
+ e_html_editor_spell_check_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG)
+
+static void
+html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog,
+ const gchar *word)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ GtkTreeView *tree_view;
+ GtkListStore *store;
+ gchar *markup;
+ GList *list, *link;
+
+ if (word == NULL)
+ return;
+
+ if (dialog->priv->word != word) {
+ g_free (dialog->priv->word);
+ dialog->priv->word = g_strdup (word);
+ }
+
+ markup = g_strdup_printf (_("<b>Suggestions for '%s'</b>"), word);
+ gtk_label_set_markup (
+ GTK_LABEL (dialog->priv->suggestion_label), markup);
+ g_free (markup);
+
+ tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (tree_view));
+ gtk_list_store_clear (store);
+
+ list = e_spell_dictionary_get_suggestions (
+ dialog->priv->current_dict, word, -1);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ GtkTreeIter iter;
+ gchar *suggestion = link->data;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, suggestion, -1);
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_free);
+
+ /* We give focus to WebKit so that the currently selected word
+ * is highlited. Without focus selection is not visible (at
+ * least with my default color scheme). The focus in fact is not
+ * given to WebKit, because this dialog is modal, but it satisfies
+ * it in a way that it paints the selection :) */
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ gtk_widget_grab_focus (GTK_WIDGET (view));
+}
+
+static gboolean
+select_next_word (EHTMLEditorSpellCheckDialog *dialog)
+{
+ WebKitDOMNode *anchor, *focus;
+ gulong anchor_offset, focus_offset;
+
+ anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection);
+ anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection);
+
+ focus = webkit_dom_dom_selection_get_focus_node (dialog->priv->selection);
+ focus_offset = webkit_dom_dom_selection_get_focus_offset (dialog->priv->selection);
+
+ /* Jump _behind_ next word */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "move", "forward", "word");
+ /* Jump before the word */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "move", "backward", "word");
+ /* Select it */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "extend", "forward", "word");
+
+ /* If the selection didn't change, then we have most probably
+ * reached the end of document - return FALSE */
+ return !((anchor == webkit_dom_dom_selection_get_anchor_node (
+ dialog->priv->selection)) &&
+ (anchor_offset == webkit_dom_dom_selection_get_anchor_offset (
+ dialog->priv->selection)) &&
+ (focus == webkit_dom_dom_selection_get_focus_node (
+ dialog->priv->selection)) &&
+ (focus_offset == webkit_dom_dom_selection_get_focus_offset (
+ dialog->priv->selection)));
+}
+
+static gboolean
+html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog)
+{
+ WebKitDOMNode *start = NULL, *end = NULL;
+ gulong start_offset, end_offset;
+
+ if (dialog->priv->word == NULL) {
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "move", "left", "documentboundary");
+ } else {
+ /* Remember last selected word */
+ start = webkit_dom_dom_selection_get_anchor_node (
+ dialog->priv->selection);
+ end = webkit_dom_dom_selection_get_focus_node (
+ dialog->priv->selection);
+ start_offset = webkit_dom_dom_selection_get_anchor_offset (
+ dialog->priv->selection);
+ end_offset = webkit_dom_dom_selection_get_focus_offset (
+ dialog->priv->selection);
+ }
+
+ while (select_next_word (dialog)) {
+ WebKitDOMRange *range;
+ WebKitSpellChecker *checker;
+ gint loc, len;
+ gchar *word;
+
+ range = webkit_dom_dom_selection_get_range_at (
+ dialog->priv->selection, 0, NULL);
+ word = webkit_dom_range_get_text (range);
+
+ checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+ webkit_spell_checker_check_spelling_of_string (
+ checker, word, &loc, &len);
+
+ /* Found misspelled word! */
+ if (loc != -1) {
+ html_editor_spell_check_dialog_set_word (dialog, word);
+ g_free (word);
+ return TRUE;
+ }
+
+ g_free (word);
+ }
+
+ /* Restore the selection to contain the last misspelled word. This is
+ * reached only when we reach the end of the document */
+ if (start && end) {
+ webkit_dom_dom_selection_set_base_and_extent (
+ dialog->priv->selection, start, start_offset,
+ end, end_offset, NULL);
+ }
+
+ /* Close the dialog */
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ return FALSE;
+}
+
+static gboolean
+select_previous_word (EHTMLEditorSpellCheckDialog *dialog)
+{
+ WebKitDOMNode *old_anchor_node;
+ WebKitDOMNode *new_anchor_node;
+ gulong old_anchor_offset;
+ gulong new_anchor_offset;
+
+ old_anchor_node = webkit_dom_dom_selection_get_anchor_node (
+ dialog->priv->selection);
+ old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
+ dialog->priv->selection);
+
+ /* Jump on the beginning of current word */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "move", "backward", "word");
+ /* Jump before previous word */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "move", "backward", "word");
+ /* Select it */
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection, "extend", "forward", "word");
+
+ /* If the selection start didn't change, then we have most probably
+ * reached the beginnig of document. Return FALSE */
+
+ new_anchor_node = webkit_dom_dom_selection_get_anchor_node (
+ dialog->priv->selection);
+ new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
+ dialog->priv->selection);
+
+ return (new_anchor_node != old_anchor_node) ||
+ (new_anchor_offset != old_anchor_offset);
+}
+
+static gboolean
+html_editor_spell_check_dialog_prev (EHTMLEditorSpellCheckDialog *dialog)
+{
+ WebKitDOMNode *start = NULL, *end = NULL;
+ gulong start_offset, end_offset;
+
+ if (dialog->priv->word == NULL) {
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection,
+ "move", "right", "documentboundary");
+ webkit_dom_dom_selection_modify (
+ dialog->priv->selection,
+ "extend", "backward", "word");
+ } else {
+ /* Remember last selected word */
+ start = webkit_dom_dom_selection_get_anchor_node (
+ dialog->priv->selection);
+ end = webkit_dom_dom_selection_get_focus_node (
+ dialog->priv->selection);
+ start_offset = webkit_dom_dom_selection_get_anchor_offset (
+ dialog->priv->selection);
+ end_offset = webkit_dom_dom_selection_get_focus_offset (
+ dialog->priv->selection);
+ }
+
+ while (select_previous_word (dialog)) {
+ WebKitDOMRange *range;
+ WebKitSpellChecker *checker;
+ gint loc, len;
+ gchar *word;
+
+ range = webkit_dom_dom_selection_get_range_at (
+ dialog->priv->selection, 0, NULL);
+ word = webkit_dom_range_get_text (range);
+
+ checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+ webkit_spell_checker_check_spelling_of_string (
+ checker, word, &loc, &len);
+
+ /* Found misspelled word! */
+ if (loc != -1) {
+ html_editor_spell_check_dialog_set_word (dialog, word);
+ g_free (word);
+ return TRUE;
+ }
+
+ g_free (word);
+ }
+
+ /* Restore the selection to contain the last misspelled word. This is
+ * reached only when we reach the beginning of the document */
+ if (start && end) {
+ webkit_dom_dom_selection_set_base_and_extent (
+ dialog->priv->selection, start, start_offset,
+ end, end_offset, NULL);
+ }
+
+ /* Close the dialog */
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ return FALSE;
+}
+
+static void
+html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *editor_selection;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gchar *replacement;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+
+ selection = gtk_tree_view_get_selection (
+ GTK_TREE_VIEW (dialog->priv->tree_view));
+ gtk_tree_selection_get_selected (selection, &model, &iter);
+ gtk_tree_model_get (model, &iter, 0, &replacement, -1);
+
+ e_html_editor_selection_insert_html (
+ editor_selection, replacement);
+
+ g_free (replacement);
+ html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_replace_all (EHTMLEditorSpellCheckDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *editor_selection;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gchar *replacement;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+
+ selection = gtk_tree_view_get_selection (
+ GTK_TREE_VIEW (dialog->priv->tree_view));
+ gtk_tree_selection_get_selected (selection, &model, &iter);
+ gtk_tree_model_get (model, &iter, 0, &replacement, -1);
+
+ /* Repeatedly search for 'word', then replace selection by
+ * 'replacement'. Repeat until there's at least one occurence of
+ * 'word' in the document */
+ while (webkit_web_view_search_text (
+ WEBKIT_WEB_VIEW (view), dialog->priv->word,
+ FALSE, TRUE, TRUE)) {
+
+ e_html_editor_selection_insert_html (
+ editor_selection, replacement);
+ }
+
+ g_free (replacement);
+ html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_ignore (EHTMLEditorSpellCheckDialog *dialog)
+{
+ if (dialog->priv->word == NULL)
+ return;
+
+ e_spell_dictionary_ignore_word (
+ dialog->priv->current_dict, dialog->priv->word, -1);
+
+ html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_learn (EHTMLEditorSpellCheckDialog *dialog)
+{
+ if (dialog->priv->word == NULL)
+ return;
+
+ e_spell_dictionary_learn_word (
+ dialog->priv->current_dict, dialog->priv->word, -1);
+
+ html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dialog)
+{
+ GtkComboBox *combo_box;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ ESpellDictionary *dictionary;
+
+ combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+ gtk_combo_box_get_active_iter (combo_box, &iter);
+ model = gtk_combo_box_get_model (combo_box);
+
+ gtk_tree_model_get (model, &iter, 1, &dictionary, -1);
+
+ dialog->priv->current_dict = dictionary;
+
+ /* Update suggestions */
+ html_editor_spell_check_dialog_set_word (dialog, dialog->priv->word);
+}
+
+static void
+html_editor_spell_check_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSpellCheckDialog *dialog;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+
+ dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
+
+ g_free (dialog->priv->word);
+ dialog->priv->word = NULL;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ dialog->priv->selection = webkit_dom_dom_window_get_selection (window);
+
+ /* Select the first word or quit */
+ if (html_editor_spell_check_dialog_next (dialog)) {
+ GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+ show (widget);
+ }
+}
+
+static void
+html_editor_spell_check_dialog_finalize (GObject *object)
+{
+ EHTMLEditorSpellCheckDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (object);
+
+ g_free (priv->word);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+ finalize (object);
+}
+
+static void
+html_editor_spell_check_dialog_constructed (GObject *object)
+{
+ EHTMLEditorSpellCheckDialog *dialog;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+ constructed (object);
+
+ dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (object);
+ e_html_editor_spell_check_dialog_update_dictionaries (dialog);
+}
+
+static void
+e_html_editor_spell_check_dialog_class_init (EHTMLEditorSpellCheckDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+ GObjectClass *object_class;
+
+ g_type_class_add_private (
+ class, sizeof (EHTMLEditorSpellCheckDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = html_editor_spell_check_dialog_finalize;
+ object_class->constructed = html_editor_spell_check_dialog_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_spell_check_dialog_show;
+}
+
+static void
+e_html_editor_spell_check_dialog_init (EHTMLEditorSpellCheckDialog *dialog)
+{
+ GtkWidget *widget;
+ GtkGrid *main_layout;
+ GtkListStore *store;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ dialog->priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == Suggestions == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Suggestions</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 2, 1);
+ dialog->priv->suggestion_label = widget;
+
+ /* Tree view */
+ widget = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+ gtk_widget_set_vexpand (widget, TRUE);
+ gtk_widget_set_hexpand (widget, TRUE);
+ dialog->priv->tree_view = widget;
+
+ /* Column */
+ column = gtk_tree_view_column_new_with_attributes (
+ "", gtk_cell_renderer_text_new (), "text", 0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+
+ /* Store */
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_tree_view_set_model (
+ GTK_TREE_VIEW (widget), GTK_TREE_MODEL (store));
+
+ /* Scrolled Window */
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (widget, 150, -1);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (widget), dialog->priv->tree_view);
+ gtk_grid_attach (main_layout, widget, 0, 1, 1, 5);
+
+ /* Replace */
+ widget = gtk_button_new_with_mnemonic (_("Replace"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_CONVERT, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 1, 1, 1);
+ dialog->priv->replace_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_replace), dialog);
+
+ /* Replace All */
+ widget = gtk_button_new_with_mnemonic (_("Replace All"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 2, 1, 1);
+ dialog->priv->replace_all_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_replace_all), dialog);
+
+ /* Ignore */
+ widget = gtk_button_new_with_mnemonic (_("Ignore"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 3, 1, 1);
+ dialog->priv->ignore_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_ignore), dialog);
+
+ /* Skip */
+ widget = gtk_button_new_with_mnemonic (_("Skip"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 4, 1, 1);
+ dialog->priv->skip_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_next), dialog);
+
+ /* Back */
+ widget = gtk_button_new_with_mnemonic (_("Back"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 5, 1, 1);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_prev), dialog);
+
+ /* Dictionary label */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Dictionary</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0);
+ gtk_grid_attach (main_layout, widget, 0, 6, 2, 1);
+
+ /* Dictionaries combo */
+ widget = gtk_combo_box_new ();
+ gtk_grid_attach (main_layout, widget, 0, 7, 1, 1);
+ dialog->priv->dictionary_combo = widget;
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (
+ GTK_CELL_LAYOUT (widget), renderer, TRUE);
+ gtk_cell_layout_add_attribute (
+ GTK_CELL_LAYOUT (widget), renderer, "text", 0);
+
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_spell_check_dialog_set_dictionary), dialog);
+
+ /* Add Word button */
+ widget = gtk_button_new_with_mnemonic (_("Add Word"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
+ gtk_grid_attach (main_layout, widget, 1, 7, 1, 1);
+ dialog->priv->add_word_button = widget;
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (html_editor_spell_check_dialog_learn), dialog);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_spell_check_dialog_new (EHTMLEditor *editor)
+{
+ return g_object_new (
+ E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG,
+ "editor", editor,
+ "title", N_("Spell Checking"),
+ NULL);
+}
+
+void
+e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ ESpellChecker *spell_checker;
+ GtkComboBox *combo_box;
+ GtkListStore *store;
+ GQueue queue = G_QUEUE_INIT;
+ gchar **languages;
+ guint n_languages = 0;
+ guint ii;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog));
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ spell_checker = e_html_editor_view_get_spell_checker (view);
+
+ languages = e_spell_checker_list_active_languages (
+ spell_checker, &n_languages);
+ for (ii = 0; ii < n_languages; ii++) {
+ ESpellDictionary *dictionary;
+
+ dictionary = e_spell_checker_ref_dictionary (
+ spell_checker, languages[ii]);
+ if (dictionary != NULL)
+ g_queue_push_tail (&queue, dictionary);
+ else
+ g_warning (
+ "%s: No '%s' dictionary found",
+ G_STRFUNC, languages[ii]);
+ }
+ g_strfreev (languages);
+
+ /* Populate a list store for the combo box. */
+ store = gtk_list_store_new (
+ NUM_COLUMNS,
+ G_TYPE_STRING, /* COLUMN_NAME */
+ E_TYPE_SPELL_DICTIONARY); /* COLUMN_DICTIONARY */
+
+ while (!g_queue_is_empty (&queue)) {
+ ESpellDictionary *dictionary;
+ GtkTreeIter iter;
+ const gchar *name;
+
+ dictionary = g_queue_pop_head (&queue);
+ name = e_spell_dictionary_get_name (dictionary);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (
+ store, &iter,
+ COLUMN_NAME, name,
+ COLUMN_DICTIONARY, dictionary,
+ -1);
+
+ g_object_unref (dictionary);
+ }
+
+ /* FIXME Try to restore selection. */
+ combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+ gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+ gtk_combo_box_set_active (combo_box, 0);
+
+ g_object_unref (store);
+}
+
diff --git a/e-util/e-html-editor-spell-check-dialog.h b/e-util/e-html-editor-spell-check-dialog.h
new file mode 100644
index 0000000000..4191cd98ad
--- /dev/null
+++ b/e-util/e-html-editor-spell-check-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-spell-check-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_SPELL_CHECK_DIALOG_H
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG \
+ (e_html_editor_spell_check_dialog_get_type ())
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialog))
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogClass))
+#define E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG))
+#define E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG))
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorSpellCheckDialog EHTMLEditorSpellCheckDialog;
+typedef struct _EHTMLEditorSpellCheckDialogClass EHTMLEditorSpellCheckDialogClass;
+typedef struct _EHTMLEditorSpellCheckDialogPrivate EHTMLEditorSpellCheckDialogPrivate;
+
+struct _EHTMLEditorSpellCheckDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorSpellCheckDialogPrivate *priv;
+};
+
+struct _EHTMLEditorSpellCheckDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_spell_check_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_spell_check_dialog_new
+ (EHTMLEditor *editor);
+void e_html_editor_spell_check_dialog_update_dictionaries
+ (EHTMLEditorSpellCheckDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_SPELL_CHECK_DIALOG_H */
diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c
new file mode 100644
index 0000000000..467d2a62e8
--- /dev/null
+++ b/e-util/e-html-editor-table-dialog.c
@@ -0,0 +1,866 @@
+/*
+ * e-html-editor-table-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-table-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogPrivate))
+
+struct _EHTMLEditorTableDialogPrivate {
+ GtkWidget *rows_edit;
+ GtkWidget *columns_edit;
+
+ GtkWidget *width_edit;
+ GtkWidget *width_units;
+ GtkWidget *width_check;
+
+ GtkWidget *spacing_edit;
+ GtkWidget *padding_edit;
+ GtkWidget *border_edit;
+
+ GtkWidget *alignment_combo;
+
+ GtkWidget *background_color_button;
+ GtkWidget *background_image_button;
+ GtkWidget *image_chooser_dialog;
+
+ WebKitDOMHTMLTableElement *table_element;
+};
+
+static GdkRGBA white = { 1, 1, 1, 1 };
+
+G_DEFINE_TYPE (
+ EHTMLEditorTableDialog,
+ e_html_editor_table_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static WebKitDOMElement *
+html_editor_table_dialog_create_table (EHTMLEditorTableDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorSelection *editor_selection;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *table, *br, *caret, *parent, *element;
+ gint i;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ editor_selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+
+ /* Default 3x3 table */
+ table = webkit_dom_document_create_element (document, "TABLE", NULL);
+ for (i = 0; i < 3; i++) {
+ WebKitDOMHTMLElement *row;
+ gint j;
+
+ row = webkit_dom_html_table_element_insert_row (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+ for (j = 0; j < 3; j++) {
+ webkit_dom_html_table_row_element_insert_cell (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+ }
+ }
+
+ caret = e_html_editor_selection_save_caret_position (editor_selection);
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+ element = caret;
+
+ while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ element = parent;
+ parent = webkit_dom_node_get_parent_element (
+ WEBKIT_DOM_NODE (parent));
+ }
+
+ br = webkit_dom_document_create_element (document, "BR", NULL);
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (parent),
+ WEBKIT_DOM_NODE (br),
+ webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+ NULL);
+
+ /* Insert the table into body below the caret */
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (parent),
+ WEBKIT_DOM_NODE (table),
+ webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+ NULL);
+
+ e_html_editor_selection_clear_caret_position_marker (editor_selection);
+
+ e_html_editor_view_set_changed (view, TRUE);
+
+ return table;
+}
+
+static void
+html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog)
+{
+ WebKitDOMHTMLCollection *rows;
+ gulong ii, current_count, expected_count;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+ current_count = webkit_dom_html_collection_get_length (rows);
+ expected_count = gtk_spin_button_get_value (
+ GTK_SPIN_BUTTON (dialog->priv->rows_edit));
+
+ if (current_count < expected_count) {
+ for (ii = 0; ii < expected_count - current_count; ii++) {
+ webkit_dom_html_table_element_insert_row (
+ dialog->priv->table_element, -1, NULL);
+ }
+ } else if (current_count > expected_count) {
+ for (ii = 0; ii < current_count - expected_count; ii++) {
+ webkit_dom_html_table_element_delete_row (
+ dialog->priv->table_element, -1, NULL);
+ }
+ }
+}
+
+static void
+html_editor_table_dialog_get_row_count (EHTMLEditorTableDialog *dialog)
+{
+ WebKitDOMHTMLCollection *rows;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->rows_edit),
+ webkit_dom_html_collection_get_length (rows));
+}
+
+static void
+html_editor_table_dialog_set_column_count (EHTMLEditorTableDialog *dialog)
+{
+ WebKitDOMHTMLCollection *rows;
+ gulong ii, row_count, expected_columns;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+ row_count = webkit_dom_html_collection_get_length (rows);
+ expected_columns = gtk_spin_button_get_value (
+ GTK_SPIN_BUTTON (dialog->priv->columns_edit));
+
+ for (ii = 0; ii < row_count; ii++) {
+ WebKitDOMHTMLTableRowElement *row;
+ WebKitDOMHTMLCollection *cells;
+ gulong jj, current_columns;
+
+ row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (
+ webkit_dom_html_collection_item (rows, ii));
+
+ cells = webkit_dom_html_table_row_element_get_cells (row);
+ current_columns = webkit_dom_html_collection_get_length (cells);
+
+ if (current_columns < expected_columns) {
+ for (jj = 0; jj < expected_columns - current_columns; jj++) {
+ webkit_dom_html_table_row_element_insert_cell (
+ row, -1, NULL);
+ }
+ } else if (expected_columns < current_columns) {
+ for (jj = 0; jj < current_columns - expected_columns; jj++) {
+ webkit_dom_html_table_row_element_delete_cell (
+ row, -1, NULL);
+ }
+ }
+ }
+}
+
+static void
+html_editor_table_dialog_get_column_count (EHTMLEditorTableDialog *dialog)
+{
+ WebKitDOMHTMLCollection *rows, *columns;
+ WebKitDOMNode *row;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+ row = webkit_dom_html_collection_item (rows, 0);
+
+ columns = webkit_dom_html_table_row_element_get_cells (
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->columns_edit),
+ webkit_dom_html_collection_get_length (columns));
+}
+
+static void
+html_editor_table_dialog_set_width (EHTMLEditorTableDialog *dialog)
+{
+ gchar *width;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ if (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
+ gchar *units;
+
+ units = gtk_combo_box_text_get_active_text (
+ GTK_COMBO_BOX_TEXT (dialog->priv->width_units));
+ width = g_strdup_printf (
+ "%d%s",
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+ units);
+ g_free (units);
+
+ gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+ gtk_widget_set_sensitive (dialog->priv->width_units, TRUE);
+ } else {
+ width = g_strdup ("auto");
+
+ gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
+ gtk_widget_set_sensitive (dialog->priv->width_units, FALSE);
+ }
+
+ webkit_dom_html_table_element_set_width (
+ dialog->priv->table_element, width);
+ g_free (width);
+}
+
+static void
+html_editor_table_dialog_get_width (EHTMLEditorTableDialog *dialog)
+{
+ gchar *width;
+
+ width = webkit_dom_html_table_element_get_width (dialog->priv->table_element);
+ if (!width || !*width || g_ascii_strncasecmp (width, "auto", 4) == 0) {
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), 100);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
+ } else {
+ gint width_int = atoi (width);
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), width_int);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->width_units),
+ ((strstr (width, "%") == NULL) ?
+ "units-px" : "units-percent"));
+ }
+ g_free (width);
+}
+
+static void
+html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog)
+{
+ g_return_if_fail (dialog->priv->table_element);
+
+ webkit_dom_html_table_element_set_align (
+ dialog->priv->table_element,
+ gtk_combo_box_get_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo)));
+}
+
+static void
+html_editor_table_dialog_get_alignment (EHTMLEditorTableDialog *dialog)
+{
+ gchar *alignment;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ alignment = webkit_dom_html_table_element_get_align (
+ dialog->priv->table_element);
+
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
+
+ g_free (alignment);
+}
+
+static void
+html_editor_table_dialog_set_padding (EHTMLEditorTableDialog *dialog)
+{
+ gchar *padding;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ padding = g_strdup_printf (
+ "%d",
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->padding_edit)));
+
+ webkit_dom_html_table_element_set_cell_padding (
+ dialog->priv->table_element, padding);
+
+ g_free (padding);
+}
+
+static void
+html_editor_table_dialog_get_padding (EHTMLEditorTableDialog *dialog)
+{
+ gchar *padding;
+ gint padding_int;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ padding = webkit_dom_html_table_element_get_cell_padding (
+ dialog->priv->table_element);
+ if (!padding || !*padding) {
+ padding_int = 0;
+ } else {
+ padding_int = atoi (padding);
+ }
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->padding_edit), padding_int);
+
+ g_free (padding);
+}
+
+static void
+html_editor_table_dialog_set_spacing (EHTMLEditorTableDialog *dialog)
+{
+ gchar *spacing;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ spacing = g_strdup_printf (
+ "%d",
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->spacing_edit)));
+
+ webkit_dom_html_table_element_set_cell_spacing (
+ dialog->priv->table_element, spacing);
+
+ g_free (spacing);
+}
+
+static void
+html_editor_table_dialog_get_spacing (EHTMLEditorTableDialog *dialog)
+{
+ gchar *spacing;
+ gint spacing_int;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ spacing = webkit_dom_html_table_element_get_cell_spacing (
+ dialog->priv->table_element);
+ if (!spacing || !*spacing) {
+ spacing_int = 0;
+ } else {
+ spacing_int = atoi (spacing);
+ }
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->spacing_edit), spacing_int);
+
+ g_free (spacing);
+}
+
+static void
+html_editor_table_dialog_set_border (EHTMLEditorTableDialog *dialog)
+{
+ gchar *border;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ border = g_strdup_printf (
+ "%d",
+ gtk_spin_button_get_value_as_int (
+ GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+
+ webkit_dom_html_table_element_set_border (
+ dialog->priv->table_element, border);
+
+ g_free (border);
+}
+
+static void
+html_editor_table_dialog_get_border (EHTMLEditorTableDialog *dialog)
+{
+ gchar *border;
+ gint border_int;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ border = webkit_dom_html_table_element_get_border (
+ dialog->priv->table_element);
+ if (!border || !*border) {
+ border_int = 0;
+ } else {
+ border_int = atoi (border);
+ }
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->border_edit), border_int);
+
+ g_free (border);
+}
+
+static void
+html_editor_table_dialog_set_background_color (EHTMLEditorTableDialog *dialog)
+{
+ gchar *color;
+ GdkRGBA rgba;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
+ color = g_strdup_printf (
+ "#%06x", e_rgba_to_value (&rgba));
+
+ webkit_dom_html_table_element_set_bg_color (
+ dialog->priv->table_element, color);
+
+ g_free (color);
+}
+
+static void
+html_editor_table_dialog_get_background_color (EHTMLEditorTableDialog *dialog)
+{
+ gchar *color;
+ GdkRGBA rgba;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ color = webkit_dom_html_table_element_get_bg_color (
+ dialog->priv->table_element);
+
+ gdk_rgba_parse (&rgba, color);
+
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
+
+ g_free (color);
+}
+
+static void
+html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog)
+{
+ const gchar *filename;
+
+ g_return_if_fail (dialog->priv->table_element);
+
+ filename = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+
+ if (filename) {
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->table_element),
+ "background", filename, NULL);
+ } else {
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->table_element),
+ "background");
+ }
+}
+
+static void
+html_editor_table_dialog_get_background_image (EHTMLEditorTableDialog *dialog)
+{
+ g_return_if_fail (dialog->priv->table_element);
+
+ if (!webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background")) {
+
+ gtk_file_chooser_unselect_all (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+ return;
+ } else {
+ gchar *background;
+
+ background = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background");
+
+ gtk_file_chooser_set_filename (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_button),
+ background);
+
+ g_free (background);
+ }
+}
+
+static void
+html_editor_table_dialog_get_values (EHTMLEditorTableDialog *dialog)
+{
+ html_editor_table_dialog_get_row_count (dialog);
+ html_editor_table_dialog_get_column_count (dialog);
+ html_editor_table_dialog_get_width (dialog);
+ html_editor_table_dialog_get_alignment (dialog);
+ html_editor_table_dialog_get_spacing (dialog);
+ html_editor_table_dialog_get_padding (dialog);
+ html_editor_table_dialog_get_border (dialog);
+ html_editor_table_dialog_get_background_color (dialog);
+ html_editor_table_dialog_get_background_image (dialog);
+}
+
+static void
+html_editor_table_dialog_reset_values (EHTMLEditorTableDialog *dialog)
+{
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->rows_edit), 3);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->columns_edit), 3);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->width_edit), 100);
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
+
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->spacing_edit), 2);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->padding_edit), 1);
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (dialog->priv->border_edit), 1);
+
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->background_color_button), &white);
+ gtk_file_chooser_unselect_all (
+ GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+
+ html_editor_table_dialog_set_row_count (dialog);
+ html_editor_table_dialog_set_column_count (dialog);
+ html_editor_table_dialog_set_width (dialog);
+ html_editor_table_dialog_set_alignment (dialog);
+ html_editor_table_dialog_set_spacing (dialog);
+ html_editor_table_dialog_set_padding (dialog);
+ html_editor_table_dialog_set_border (dialog);
+ html_editor_table_dialog_set_background_color (dialog);
+ html_editor_table_dialog_set_background_image (dialog);
+}
+
+static void
+html_editor_table_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorTableDialog *dialog;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *selection;
+
+ dialog = E_HTML_EDITOR_TABLE_DIALOG (widget);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ selection = webkit_dom_dom_window_get_selection (window);
+ if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
+ WebKitDOMElement *table;
+ WebKitDOMRange *range;
+
+ range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+ table = e_html_editor_dom_node_find_parent_element (
+ webkit_dom_range_get_start_container (range, NULL), "TABLE");
+
+ if (!table) {
+ dialog->priv->table_element = WEBKIT_DOM_HTML_TABLE_ELEMENT (
+ html_editor_table_dialog_create_table (dialog));
+ html_editor_table_dialog_reset_values (dialog);
+ } else {
+ dialog->priv->table_element =
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table);
+ html_editor_table_dialog_get_values (dialog);
+ }
+ }
+
+ /* Chain up to parent implementation */
+ GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_table_dialog_hide (GtkWidget *widget)
+{
+ EHTMLEditorTableDialogPrivate *priv;
+
+ priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (widget);
+
+ priv->table_element = NULL;
+
+ GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
+}
+
+static void
+e_html_editor_table_dialog_class_init (EHTMLEditorTableDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorTableDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_table_dialog_show;
+ widget_class->hide = html_editor_table_dialog_hide;
+}
+
+static void
+e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog)
+{
+ GtkGrid *main_layout, *grid;
+ GtkWidget *widget;
+ GtkFileFilter *file_filter;
+
+ dialog->priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* == General == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Rows */
+ widget = gtk_image_new_from_icon_name ("stock_select-row", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ widget = gtk_spin_button_new_with_range (1, G_MAXINT, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_row_count), dialog);
+ dialog->priv->rows_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Rows:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->rows_edit);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+ /* Columns */
+ widget = gtk_image_new_from_icon_name ("stock_select-column", GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+
+ widget = gtk_spin_button_new_with_range (1, G_MAXINT, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_column_count), dialog);
+ dialog->priv->columns_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("C_olumns:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->columns_edit);
+ gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+ /* == Layout == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Width */
+ widget = gtk_check_button_new_with_mnemonic (_("_Width:"));
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+ dialog->priv->width_check = widget;
+
+ widget = gtk_spin_button_new_with_range (1, 100, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+ dialog->priv->width_edit = widget;
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+ gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+ dialog->priv->width_units = widget;
+
+ /* Spacing */
+ widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_spacing), dialog);
+ dialog->priv->spacing_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Spacing:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->spacing_edit);
+ gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 0, 1, 1);
+
+ /* Padding */
+ widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 5, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_padding), dialog);
+ dialog->priv->padding_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Padding:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->padding_edit);
+ gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 1, 1, 1);
+
+ /* Border */
+ widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+ gtk_grid_attach (grid, widget, 5, 2, 1, 1);
+ g_signal_connect_swapped (
+ widget, "value-changed",
+ G_CALLBACK (html_editor_table_dialog_set_border), dialog);
+ dialog->priv->border_edit = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Border:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->border_edit);
+ gtk_grid_attach (grid, widget, 4, 2, 1, 1);
+
+ widget = gtk_label_new ("px");
+ gtk_grid_attach (grid, widget, 6, 2, 1, 1);
+
+ /* Alignment */
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+ gtk_grid_attach (grid, widget, 1, 1, 2, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_table_dialog_set_alignment), dialog);
+ dialog->priv->alignment_combo = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Alignment:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->alignment_combo);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ /* == Background == */
+ widget = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background</b>"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_row_spacing (grid, 5);
+ gtk_grid_set_column_spacing (grid, 5);
+ gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+ gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+ /* Color */
+ widget = e_color_combo_new ();
+ e_color_combo_set_default_color (E_COLOR_COMBO (widget), &white);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_table_dialog_set_background_color), dialog);
+ dialog->priv->background_color_button = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Color:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_color_button);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+ /* Image */
+ widget = e_image_chooser_dialog_new (
+ _("Choose Background Image"),
+ GTK_WINDOW (dialog));
+ dialog->priv->image_chooser_dialog = widget;
+
+ file_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (file_filter, _("Images"));
+ gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+ widget = gtk_file_chooser_button_new_with_dialog (
+ dialog->priv->image_chooser_dialog);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), file_filter);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "file-set",
+ G_CALLBACK (html_editor_table_dialog_set_background_image), dialog);
+ dialog->priv->background_image_button = widget;
+
+ widget =gtk_label_new_with_mnemonic (_("Image:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->background_image_button);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_table_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_TABLE_DIALOG,
+ "editor", editor,
+ "title", N_("Table Properties"),
+ NULL));
+}
+
diff --git a/e-util/e-html-editor-table-dialog.h b/e-util/e-html-editor-table-dialog.h
new file mode 100644
index 0000000000..70c790d22d
--- /dev/null
+++ b/e-util/e-html-editor-table-dialog.h
@@ -0,0 +1,69 @@
+/*
+ * e-html-editor-table-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_TABLE_DIALOG_H
+#define E_HTML_EDITOR_TABLE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_TABLE_DIALOG \
+ (e_html_editor_table_dialog_get_type ())
+#define E_HTML_EDITOR_TABLE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialog))
+#define E_HTML_EDITOR_TABLE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogClass))
+#define E_IS_HTML_EDITOR_TABLE_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG))
+#define E_IS_HTML_EDITOR_TABLE_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_TABLE_DIALOG))
+#define E_HTML_EDITOR_TABLE_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorTableDialog EHTMLEditorTableDialog;
+typedef struct _EHTMLEditorTableDialogClass EHTMLEditorTableDialogClass;
+typedef struct _EHTMLEditorTableDialogPrivate EHTMLEditorTableDialogPrivate;
+
+struct _EHTMLEditorTableDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorTableDialogPrivate *priv;
+};
+
+struct _EHTMLEditorTableDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_table_dialog_get_type (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_table_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_TABLE_DIALOG_H */
diff --git a/e-util/e-html-editor-text-dialog.c b/e-util/e-html-editor-text-dialog.c
new file mode 100644
index 0000000000..2db792c70a
--- /dev/null
+++ b/e-util/e-html-editor-text-dialog.c
@@ -0,0 +1,298 @@
+/*
+ * e-html-editor-text-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-text-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+
+#define E_HTML_EDITOR_TEXT_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogPrivate))
+
+struct _EHTMLEditorTextDialogPrivate {
+ GtkWidget *bold_check;
+ GtkWidget *italic_check;
+ GtkWidget *underline_check;
+ GtkWidget *strikethrough_check;
+
+ GtkWidget *color_check;
+ GtkWidget *size_check;
+};
+
+G_DEFINE_TYPE (
+ EHTMLEditorTextDialog,
+ e_html_editor_text_dialog,
+ E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_text_dialog_set_bold (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_set_bold (
+ selection,
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->bold_check)));
+}
+
+static void
+html_editor_text_dialog_set_italic (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_set_italic (
+ selection,
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->italic_check)));
+}
+
+static void
+html_editor_text_dialog_set_underline (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_set_underline (
+ selection,
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->underline_check)));
+}
+
+static void
+html_editor_text_dialog_set_strikethrough (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_set_strikethrough (
+ selection,
+ gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check)));
+}
+
+static void
+html_editor_text_dialog_set_color (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ GdkRGBA rgba;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_color_combo_get_current_color (
+ E_COLOR_COMBO (dialog->priv->color_check), &rgba);
+ e_html_editor_selection_set_font_color (selection, &rgba);
+}
+
+static void
+html_editor_text_dialog_set_size (EHTMLEditorTextDialog *dialog)
+{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ gint size;
+
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ size = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->size_check));
+
+ e_html_editor_selection_set_font_size (selection, size + 1);
+}
+
+static void
+html_editor_text_dialog_show (GtkWidget *widget)
+{
+ EHTMLEditorTextDialog *dialog;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ GdkRGBA rgba;
+
+ dialog = E_HTML_EDITOR_TEXT_DIALOG (widget);
+ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->bold_check),
+ e_html_editor_selection_is_bold (selection));
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->italic_check),
+ e_html_editor_selection_is_italic (selection));
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->underline_check),
+ e_html_editor_selection_is_underline (selection));
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check),
+ e_html_editor_selection_is_strikethrough (selection));
+
+ gtk_combo_box_set_active (
+ GTK_COMBO_BOX (dialog->priv->size_check),
+ e_html_editor_selection_get_font_size (selection));
+
+ e_html_editor_selection_get_font_color (selection, &rgba);
+ e_color_combo_set_current_color (
+ E_COLOR_COMBO (dialog->priv->color_check), &rgba);
+
+ GTK_WIDGET_CLASS (e_html_editor_text_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_text_dialog_class_init (EHTMLEditorTextDialogClass *class)
+{
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorTextDialogPrivate));
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->show = html_editor_text_dialog_show;
+}
+
+static void
+e_html_editor_text_dialog_init (EHTMLEditorTextDialog *dialog)
+{
+ GtkGrid *main_layout;
+ GtkWidget *widget;
+
+ dialog->priv = E_HTML_EDITOR_TEXT_DIALOG_GET_PRIVATE (dialog);
+
+ main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+ /* Bold */
+ widget = gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Bold"));
+ gtk_grid_attach (main_layout, widget, 1, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_text_dialog_set_bold), dialog);
+ dialog->priv->bold_check = widget;
+
+ /* Italic */
+ widget = gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Italic"));
+ gtk_grid_attach (main_layout, widget, 1, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_text_dialog_set_italic), dialog);
+ dialog->priv->italic_check = widget;
+
+ /* Underline */
+ widget = gtk_image_new_from_stock (GTK_STOCK_UNDERLINE, GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Underline"));
+ gtk_grid_attach (main_layout, widget, 1, 2, 1, 1);
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_text_dialog_set_underline), dialog);
+ dialog->priv->underline_check = widget;
+
+ widget = gtk_image_new_from_stock (GTK_STOCK_STRIKETHROUGH, GTK_ICON_SIZE_BUTTON);
+ gtk_grid_attach (main_layout, widget, 0, 3, 1, 1);
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Strikethrough"));
+ gtk_grid_attach (main_layout, widget, 1, 3, 1, 1);
+ g_signal_connect_swapped (
+ widget, "toggled",
+ G_CALLBACK (html_editor_text_dialog_set_strikethrough), dialog);
+ dialog->priv->strikethrough_check = widget;
+
+ /* Color */
+ widget = e_color_combo_new ();
+ gtk_grid_attach (main_layout, widget, 3, 0, 1, 1);
+ g_signal_connect_swapped (
+ widget, "notify::current-color",
+ G_CALLBACK (html_editor_text_dialog_set_color), dialog);
+ dialog->priv->color_check = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Color:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->color_check);
+ gtk_grid_attach (main_layout, widget, 2, 0, 1, 1);
+
+ /* Size */
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-two", "-2");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-one", "-1");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-zero", "0");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-one", "+1");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-two", "+2");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-three", "+3");
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-four", "+4");
+ gtk_grid_attach (main_layout, widget, 3, 1, 1, 1);
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (html_editor_text_dialog_set_size), dialog);
+ dialog->priv->size_check = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("Si_ze:"));
+ gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_check);
+ gtk_grid_attach (main_layout, widget, 2, 1, 1, 1);
+
+ gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_text_dialog_new (EHTMLEditor *editor)
+{
+ return GTK_WIDGET (
+ g_object_new (
+ E_TYPE_HTML_EDITOR_TEXT_DIALOG,
+ "editor", editor,
+ "title", N_("Text Properties"),
+ NULL));
+}
diff --git a/e-util/e-html-editor-text-dialog.h b/e-util/e-html-editor-text-dialog.h
new file mode 100644
index 0000000000..006b780009
--- /dev/null
+++ b/e-util/e-html-editor-text-dialog.h
@@ -0,0 +1,69 @@
+/*
+ * e-html-editor-text-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_TEXT_DIALOG_H
+#define E_HTML_EDITOR_TEXT_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_TEXT_DIALOG \
+ (e_html_editor_text_dialog_get_type ())
+#define E_HTML_EDITOR_TEXT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialog))
+#define E_HTML_EDITOR_TEXT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogClass))
+#define E_IS_HTML_EDITOR_TEXT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG))
+#define E_IS_HTML_EDITOR_TEXT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_TEXT_DIALOG))
+#define E_HTML_EDITOR_TEXT_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorTextDialog EHTMLEditorTextDialog;
+typedef struct _EHTMLEditorTextDialogClass EHTMLEditorTextDialogClass;
+typedef struct _EHTMLEditorTextDialogPrivate EHTMLEditorTextDialogPrivate;
+
+struct _EHTMLEditorTextDialog {
+ EHTMLEditorDialog parent;
+ EHTMLEditorTextDialogPrivate *priv;
+};
+
+struct _EHTMLEditorTextDialogClass {
+ EHTMLEditorDialogClass parent_class;
+};
+
+GType e_html_editor_text_dialog_get_type (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_text_dialog_new (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_TEXT_DIALOG_H */
diff --git a/e-util/e-html-editor-utils.c b/e-util/e-html-editor-utils.c
new file mode 100644
index 0000000000..2807ea94b0
--- /dev/null
+++ b/e-util/e-html-editor-utils.c
@@ -0,0 +1,116 @@
+/*
+ * e-html-editor-utils.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-utils.h"
+#include <string.h>
+
+/**
+ * e_html_editor_dom_node_find_parent_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search
+ *
+ * Recursively searches for first occurance of element with given @tagname
+ * that is parent of given @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing parent of @node or
+ * @NULL when @node has no parent with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+e_html_editor_dom_node_find_parent_element (WebKitDOMNode *node,
+ const gchar *tagname)
+{
+ gint taglen = strlen (tagname);
+
+ while (node) {
+
+ if (WEBKIT_DOM_IS_ELEMENT (node)) {
+ gchar *node_tagname;
+
+ node_tagname = webkit_dom_element_get_tag_name (
+ WEBKIT_DOM_ELEMENT (node));
+
+ if (node_tagname &&
+ (strlen (node_tagname) == taglen) &&
+ (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+ g_free (node_tagname);
+ return WEBKIT_DOM_ELEMENT (node);
+ }
+
+ g_free (node_tagname);
+ }
+
+ node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+ }
+
+ return NULL;
+}
+
+/**
+ * e_html_editor_dom_node_find_child_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search.
+ *
+ * Recursively searches for first occurence of element with given @tagname that
+ * is a child of @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing a child of @node or
+ * @NULL when @node has no child with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+e_html_editor_dom_node_find_child_element (WebKitDOMNode *node,
+ const gchar *tagname)
+{
+ WebKitDOMNode *start_node = node;
+ gint taglen = strlen (tagname);
+
+ do {
+ if (WEBKIT_DOM_IS_ELEMENT (node)) {
+ gchar *node_tagname;
+
+ node_tagname = webkit_dom_element_get_tag_name (
+ WEBKIT_DOM_ELEMENT (node));
+
+ if (node_tagname &&
+ (strlen (node_tagname) == taglen) &&
+ (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+ g_free (node_tagname);
+ return WEBKIT_DOM_ELEMENT (node);
+ }
+
+ g_free (node_tagname);
+ }
+
+ if (webkit_dom_node_has_child_nodes (node)) {
+ node = webkit_dom_node_get_first_child (node);
+ } else if (webkit_dom_node_get_next_sibling (node)) {
+ node = webkit_dom_node_get_next_sibling (node);
+ } else {
+ node = webkit_dom_node_get_parent_node (node);
+ }
+ } while (!webkit_dom_node_is_same_node (node, start_node));
+
+ return NULL;
+}
diff --git a/e-util/e-html-editor-utils.h b/e-util/e-html-editor-utils.h
new file mode 100644
index 0000000000..7331a87709
--- /dev/null
+++ b/e-util/e-html-editor-utils.h
@@ -0,0 +1,44 @@
+/*
+ * e-html-editor-utils.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_UTILS_H
+#define E_HTML_EDITOR_UTILS_H
+
+#include <webkit/webkitdom.h>
+
+G_BEGIN_DECLS
+
+WebKitDOMElement *
+ e_html_editor_dom_node_find_parent_element
+ (WebKitDOMNode *node,
+ const gchar *tagname);
+
+WebKitDOMElement *
+ e_html_editor_dom_node_find_child_element
+ (WebKitDOMNode *node,
+ const gchar *tagname);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_UTILS_H */
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c
new file mode 100644
index 0000000000..21cadb3a5e
--- /dev/null
+++ b/e-util/e-html-editor-view.c
@@ -0,0 +1,6303 @@
+/*
+ * e-html-editor-view.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-view.h"
+#include "e-html-editor.h"
+#include "e-emoticon-chooser.h"
+
+#include <e-util/e-util.h>
+#include <e-util/e-marshal.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#define E_HTML_EDITOR_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewPrivate))
+
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+
+#define URL_PATTERN \
+ "((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[\\-;:&=\\+\\$,\\w]+@)?" \
+ "[A-Za-z0-9\\.\\-]+|(?:www\\.|[\\-;:&=\\+\\$,\\w]+@)" \
+ "[A-Za-z0-9\\.\\-]+)((?:\\/[\\+~%\\/\\.\\w\\-]*)?\\?" \
+ "?(?:[\\-\\+=&;%@\\.\\w]*)#?(?:[\\.\\!\\/\\\\w]*))?)"
+
+#define URL_PATTERN_SPACE URL_PATTERN "\\s"
+
+#define QUOTE_SYMBOL ">"
+
+/* Keep synchronized with the same value in EHTMLEditorSelection */
+#define SPACES_PER_LIST_LEVEL 8
+
+/**
+ * EHTMLEditorView:
+ *
+ * The #EHTMLEditorView is a WebKit-based rich text editor. The view itself
+ * only provides means to configure global behavior of the editor. To work
+ * with the actual content, current cursor position or current selection,
+ * use #EHTMLEditorSelection object.
+ */
+
+struct _EHTMLEditorViewPrivate {
+ gint changed : 1;
+ gint inline_spelling : 1;
+ gint magic_links : 1;
+ gint magic_smileys : 1;
+ gint can_copy : 1;
+ gint can_cut : 1;
+ gint can_paste : 1;
+ gint can_redo : 1;
+ gint can_undo : 1;
+ gint reload_in_progress : 1;
+ gint html_mode : 1;
+
+ EHTMLEditorSelection *selection;
+
+ WebKitDOMElement *element_under_mouse;
+
+ GHashTable *inline_images;
+
+ GSettings *font_settings;
+ GSettings *aliasing_settings;
+
+ gboolean convertor_insert;
+
+ WebKitWebView *convertor_web_view;
+};
+
+enum {
+ PROP_0,
+ PROP_CAN_COPY,
+ PROP_CAN_CUT,
+ PROP_CAN_PASTE,
+ PROP_CAN_REDO,
+ PROP_CAN_UNDO,
+ PROP_CHANGED,
+ PROP_HTML_MODE,
+ PROP_INLINE_SPELLING,
+ PROP_MAGIC_LINKS,
+ PROP_MAGIC_SMILEYS,
+ PROP_SPELL_CHECKER
+};
+
+enum {
+ POPUP_EVENT,
+ PASTE_PRIMARY_CLIPBOARD,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static CamelDataCache *emd_global_http_cache = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (
+ EHTMLEditorView,
+ e_html_editor_view,
+ WEBKIT_TYPE_WEB_VIEW,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EXTENSIBLE, NULL))
+
+static WebKitDOMRange *
+html_editor_view_get_dom_range (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *selection;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ selection = webkit_dom_dom_window_get_selection (window);
+
+ if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+ return NULL;
+ }
+
+ return webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+}
+
+static void
+html_editor_view_user_changed_contents_cb (EHTMLEditorView *view,
+ gpointer user_data)
+{
+ WebKitWebView *web_view;
+ gboolean can_redo, can_undo;
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ e_html_editor_view_set_changed (view, TRUE);
+
+ can_redo = webkit_web_view_can_redo (web_view);
+ if (view->priv->can_redo != can_redo) {
+ view->priv->can_redo = can_redo;
+ g_object_notify (G_OBJECT (view), "can-redo");
+ }
+
+ can_undo = webkit_web_view_can_undo (web_view);
+ if (view->priv->can_undo != can_undo) {
+ view->priv->can_undo = can_undo;
+ g_object_notify (G_OBJECT (view), "can-undo");
+ }
+}
+
+static void
+html_editor_view_selection_changed_cb (EHTMLEditorView *view,
+ gpointer user_data)
+{
+ WebKitWebView *web_view;
+ gboolean can_copy, can_cut, can_paste;
+
+ web_view = WEBKIT_WEB_VIEW (view);
+
+ /* When the webview is being (re)loaded, the document is in an
+ * inconsistant state and there is no selection, so don't propagate
+ * the signal further to EHTMLEditorSelection and others and wait until
+ * the load is finished. */
+ if (view->priv->reload_in_progress) {
+ g_signal_stop_emission_by_name (view, "selection-changed");
+ return;
+ }
+
+ can_copy = webkit_web_view_can_copy_clipboard (web_view);
+ if (view->priv->can_copy != can_copy) {
+ view->priv->can_copy = can_copy;
+ g_object_notify (G_OBJECT (view), "can-copy");
+ }
+
+ can_cut = webkit_web_view_can_cut_clipboard (web_view);
+ if (view->priv->can_cut != can_cut) {
+ view->priv->can_cut = can_cut;
+ g_object_notify (G_OBJECT (view), "can-cut");
+ }
+
+ can_paste = webkit_web_view_can_paste_clipboard (web_view);
+ if (view->priv->can_paste != can_paste) {
+ view->priv->can_paste = can_paste;
+ g_object_notify (G_OBJECT (view), "can-paste");
+ }
+}
+
+static gboolean
+html_editor_view_should_show_delete_interface_for_element (EHTMLEditorView *view,
+ WebKitDOMHTMLElement *element)
+{
+ return FALSE;
+}
+
+void
+e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *view)
+{
+ EHTMLEditorSelection *selection;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMElement *caret, *parent, *element;
+ WebKitDOMRange *end_range, *actual;
+ WebKitDOMText *text;
+
+ 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);
+
+ element = webkit_dom_document_query_selector (
+ document, "body[spellcheck=true]", NULL);
+
+ if (!element)
+ return;
+
+ selection = e_html_editor_view_get_selection (view);
+ caret = e_html_editor_selection_save_caret_position (selection);
+
+ /* Block callbacks of selection-changed signal as we don't want to
+ * recount all the block format things in EHTMLEditorSelection and here as well
+ * when we are moving with caret */
+ g_signal_handlers_block_by_func (
+ view, html_editor_view_selection_changed_cb, NULL);
+ e_html_editor_selection_block_selection_changed (selection);
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+ element = caret;
+
+ while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ element = parent;
+ parent = webkit_dom_node_get_parent_element (
+ WEBKIT_DOM_NODE (parent));
+ }
+
+ /* Append some text on the end of the element */
+ text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (text), NULL);
+
+ /* Create range that's pointing on the end of this text */
+ end_range = webkit_dom_document_create_range (document);
+ webkit_dom_range_select_node_contents (
+ end_range, WEBKIT_DOM_NODE (text), NULL);
+ webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+ /* Move on the beginning of the paragraph */
+ actual = webkit_dom_document_create_range (document);
+ webkit_dom_range_select_node_contents (
+ actual, WEBKIT_DOM_NODE (element), NULL);
+ webkit_dom_range_collapse (actual, TRUE, NULL);
+ webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+ webkit_dom_dom_selection_add_range (dom_selection, actual);
+
+ /* Go through all words to spellcheck them. To avoid this we have to wait for
+ * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+ actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ /* We are moving forward word by word until we hit the text on the end of
+ * the paragraph that we previously inserted there */
+ while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
+ webkit_dom_dom_selection_modify (
+ dom_selection, "move", "forward", "word");
+ actual = webkit_dom_dom_selection_get_range_at (
+ dom_selection, 0, NULL);
+ }
+
+ /* Remove the text that we inserted on the end of the paragraph */
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (text), NULL);
+
+ /* Unblock the callbacks */
+ g_signal_handlers_unblock_by_func (
+ view, html_editor_view_selection_changed_cb, NULL);
+ e_html_editor_selection_unblock_selection_changed (selection);
+
+ e_html_editor_selection_restore_caret_position (selection);
+}
+
+static void
+move_caret_into_element (WebKitDOMDocument *document,
+ WebKitDOMElement *element)
+{
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *window_selection;
+ WebKitDOMRange *new_range;
+
+ if (!element)
+ return;
+
+ window = webkit_dom_document_get_default_view (document);
+ window_selection = webkit_dom_dom_window_get_selection (window);
+ new_range = webkit_dom_document_create_range (document);
+
+ webkit_dom_range_select_node_contents (
+ new_range, WEBKIT_DOM_NODE (element), NULL);
+ webkit_dom_range_collapse (new_range, FALSE, NULL);
+ webkit_dom_dom_selection_remove_all_ranges (window_selection);
+ webkit_dom_dom_selection_add_range (window_selection, new_range);
+}
+
+static void
+refresh_spell_check (EHTMLEditorView *view,
+ gboolean enable_spell_check)
+{
+ EHTMLEditorSelection *selection;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMRange *end_range, *actual;
+ WebKitDOMText *text;
+
+ 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);
+
+ /* Enable/Disable spellcheck in composer */
+ body = webkit_dom_document_get_body (document);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body),
+ "spellcheck",
+ enable_spell_check ? "true" : "false",
+ NULL);
+
+ selection = e_html_editor_view_get_selection (view);
+ e_html_editor_selection_save_caret_position (selection);
+
+ /* Sometimes the web view is not event focused, so we have to move caret
+ * into body */
+ if (!webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position")) {
+ move_caret_into_element (
+ document,
+ WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)));
+ e_html_editor_selection_save_caret_position (selection);
+ }
+
+ /* Block callbacks of selection-changed signal as we don't want to
+ * recount all the block format things in EHTMLEditorSelection and here as well
+ * when we are moving with caret */
+ g_signal_handlers_block_by_func (
+ view, html_editor_view_selection_changed_cb, NULL);
+ e_html_editor_selection_block_selection_changed (selection);
+
+ /* Append some text on the end of the body */
+ text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+ /* Create range that's pointing on the end of this text */
+ end_range = webkit_dom_document_create_range (document);
+ webkit_dom_range_select_node_contents (
+ end_range, WEBKIT_DOM_NODE (text), NULL);
+ webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+ /* Move on the beginning of the document */
+ webkit_dom_dom_selection_modify (
+ dom_selection, "move", "backward", "documentboundary");
+
+ /* Go through all words to spellcheck them. To avoid this we have to wait for
+ * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+ actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ /* We are moving forward word by word until we hit the text on the end of
+ * the body that we previously inserted there */
+ while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
+ webkit_dom_dom_selection_modify (
+ dom_selection, "move", "forward", "word");
+ actual = webkit_dom_dom_selection_get_range_at (
+ dom_selection, 0, NULL);
+ }
+
+ /* Remove the text that we inserted on the end of the body */
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+ /* Unblock the callbacks */
+ g_signal_handlers_unblock_by_func (
+ view, html_editor_view_selection_changed_cb, NULL);
+ e_html_editor_selection_unblock_selection_changed (selection);
+
+ e_html_editor_selection_restore_caret_position (selection);
+}
+
+void
+e_html_editor_view_turn_spell_check_off (EHTMLEditorView *view)
+{
+ refresh_spell_check (view, FALSE);
+}
+
+void
+e_html_editor_view_force_spell_check (EHTMLEditorView *view)
+{
+ refresh_spell_check (view, TRUE);
+}
+
+static void
+body_input_event_cb (WebKitDOMElement *element,
+ WebKitDOMEvent *event,
+ EHTMLEditorView *view)
+{
+ WebKitDOMNode *node;
+ WebKitDOMRange *range = html_editor_view_get_dom_range (view);
+
+ e_html_editor_view_set_changed (view, TRUE);
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+
+ /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE
+ * to move caret into right space. When this callback is called it is not
+ * necassary anymore so remove it */
+ if (e_html_editor_view_get_html_mode (view)) {
+ WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node);
+
+ if (parent) {
+ WebKitDOMNode *prev_sibling;
+
+ prev_sibling = webkit_dom_node_get_previous_sibling (
+ WEBKIT_DOM_NODE (parent));
+
+ if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) {
+ gchar *text = webkit_dom_node_get_text_content (
+ prev_sibling);
+
+ if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (
+ prev_sibling),
+ prev_sibling,
+ NULL);
+ }
+ g_free (text);
+ }
+
+ }
+ }
+
+ /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */
+ if (WEBKIT_DOM_IS_TEXT (node)) {
+ gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
+ glong length = g_utf8_strlen (text, -1);
+ WebKitDOMNode *parent;
+
+ /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
+ * character as when we will remove it it will collapse */
+ if (length > 1) {
+ if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE))
+ webkit_dom_character_data_replace_data (
+ WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
+ else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+ webkit_dom_character_data_replace_data (
+ WEBKIT_DOM_CHARACTER_DATA (node), length - 1, 1, "", NULL);
+ }
+ g_free (text);
+
+ parent = webkit_dom_node_get_parent_node (node);
+ if ((WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) ||
+ WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) &&
+ !element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph")) {
+ if (e_html_editor_view_get_html_mode (view)) {
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph");
+ } else {
+ e_html_editor_selection_set_paragraph_style (
+ e_html_editor_view_get_selection (view),
+ WEBKIT_DOM_ELEMENT (parent),
+ -1, 0, "");
+ }
+ }
+
+ /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the
+ * caret position to right place. It is removed when user starts typing. But
+ * when the user will press left arrow he will move the caret into
+ * smiley wrapper. If he will start to write there we have to move the written
+ * text out of the wrapper and move caret to right place */
+ if (WEBKIT_DOM_IS_ELEMENT (parent) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-wrapper")) {
+ WebKitDOMDocument *document;
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (parent),
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ webkit_dom_node_get_next_sibling (parent),
+ NULL);
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (parent),
+ node,
+ webkit_dom_node_get_next_sibling (parent),
+ NULL);
+ e_html_editor_selection_restore_caret_position (
+ e_html_editor_view_get_selection (view));
+ }
+ }
+}
+
+static void
+set_base64_to_element_attribute (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ const gchar *attribute)
+{
+ gchar *attribute_value;
+ const gchar *base64_src;
+
+ attribute_value = webkit_dom_element_get_attribute (element, attribute);
+
+ if ((base64_src = g_hash_table_lookup (view->priv->inline_images, attribute_value)) != NULL) {
+ const gchar *base64_data = strstr (base64_src, ";") + 1;
+ gchar *name;
+ glong name_length;
+
+ name_length =
+ g_utf8_strlen (base64_src, -1) -
+ g_utf8_strlen (base64_data, -1) - 1;
+ name = g_strndup (base64_src, name_length);
+
+ webkit_dom_element_set_attribute (element, "data-inline", "", NULL);
+ webkit_dom_element_set_attribute (element, "data-name", name, NULL);
+ webkit_dom_element_set_attribute (element, attribute, base64_data, NULL);
+
+ g_free (name);
+ }
+}
+
+static void
+change_cid_images_src_to_base64 (EHTMLEditorView *view)
+{
+ gint ii, length;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *document_element;
+ WebKitDOMNamedNodeMap *attributes;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ document_element = webkit_dom_document_get_document_element (document);
+
+ list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ set_base64_to_element_attribute (view, WEBKIT_DOM_ELEMENT (node), "src");
+ }
+
+ /* Namespaces */
+ attributes = webkit_dom_element_get_attributes (document_element);
+ length = webkit_dom_named_node_map_get_length (attributes);
+ for (ii = 0; ii < length; ii++) {
+ gchar *name;
+ WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+ name = webkit_dom_node_get_local_name (node);
+
+ if (g_str_has_prefix (name, "xmlns:")) {
+ const gchar *ns = name + 6;
+ gchar *attribute_ns = g_strconcat (ns, ":src", NULL);
+ gchar *selector = g_strconcat ("img[", ns, "\\:src^=\"cid:\"]", NULL);
+ gint ns_length, jj;
+
+ list = webkit_dom_document_query_selector_all (
+ document, selector, NULL);
+ ns_length = webkit_dom_node_list_get_length (list);
+ for (jj = 0; jj < ns_length; jj++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+ set_base64_to_element_attribute (
+ view, WEBKIT_DOM_ELEMENT (node), attribute_ns);
+ }
+
+ g_free (attribute_ns);
+ g_free (selector);
+ }
+ g_free (name);
+ }
+
+ list = webkit_dom_document_query_selector_all (
+ document, "[background^=\"cid:\"]", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ set_base64_to_element_attribute (
+ view, WEBKIT_DOM_ELEMENT (node), "background");
+ }
+ g_hash_table_remove_all (view->priv->inline_images);
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+put_body_in_citation (WebKitDOMDocument *document)
+{
+ WebKitDOMElement *cite_body = webkit_dom_document_query_selector (
+ document, "span.-x-evo-cite-body", NULL);
+
+ if (cite_body) {
+ WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+ gchar *inner_html, *with_citation;
+
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (cite_body),
+ NULL);
+
+ inner_html = webkit_dom_html_element_get_inner_html (body);
+ with_citation = g_strconcat (
+ "<blockquote type=\"cite\" id=\"-x-evo-main-cite\">",
+ inner_html, "</span>", NULL);
+ webkit_dom_html_element_set_inner_html (body, with_citation, NULL);
+ g_free (inner_html);
+ g_free (with_citation);
+ }
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+move_elements_to_body (WebKitDOMDocument *document)
+{
+ WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+ WebKitDOMNodeList *list;
+ gint ii;
+
+ list = webkit_dom_document_query_selector_all (
+ document, "span.-x-evo-to-body", NULL);
+ for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ while (webkit_dom_node_has_child_nodes (node)) {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ webkit_dom_node_get_first_child (node),
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (body)),
+ NULL);
+ }
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (node),
+ NULL);
+ }
+}
+
+static void
+repair_gmail_blockquotes (WebKitDOMDocument *document)
+{
+ WebKitDOMNodeList *list;
+ gint ii, length;
+
+ list = webkit_dom_document_query_selector_all (
+ document, "blockquote.gmail_quote", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class");
+ webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "style");
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node), "type", "cite", NULL);
+ }
+}
+
+static void
+html_editor_view_load_status_changed (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ WebKitLoadStatus status;
+
+ status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
+ if (status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ view->priv->reload_in_progress = FALSE;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body), "style");
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL);
+
+ put_body_in_citation (document);
+ move_elements_to_body (document);
+ repair_gmail_blockquotes (document);
+
+ /* Register on input event that is called when the content (body) is modified */
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (body),
+ "input",
+ G_CALLBACK (body_input_event_cb),
+ FALSE,
+ view);
+
+ if (view->priv->html_mode)
+ change_cid_images_src_to_base64 (view);
+}
+
+/* Based on original use_pictograms() from GtkHTML */
+static const gchar *emoticons_chars =
+ /* 0 */ "DO)(|/PQ*!"
+ /* 10 */ "S\0:-\0:\0:-\0"
+ /* 20 */ ":\0:;=-\"\0:;"
+ /* 30 */ "B\"|\0:-'\0:X"
+ /* 40 */ "\0:\0:-\0:\0:-"
+ /* 50 */ "\0:\0:-\0:\0:-"
+ /* 60 */ "\0:\0:\0:-\0:\0"
+ /* 70 */ ":-\0:\0:-\0:\0";
+static gint emoticons_states[] = {
+ /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70,
+ /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0,
+ /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20,
+ /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2,
+ /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51,
+ /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61,
+ /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0,
+ /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 };
+static const gchar *emoticons_icon_names[] = {
+ "face-angel",
+ "face-angry",
+ "face-cool",
+ "face-crying",
+ "face-devilish",
+ "face-embarrassed",
+ "face-kiss",
+ "face-laugh", /* not used */
+ "face-monkey", /* not used */
+ "face-plain",
+ "face-raspberry",
+ "face-sad",
+ "face-sick",
+ "face-smile",
+ "face-smile-big",
+ "face-smirk",
+ "face-surprise",
+ "face-tired",
+ "face-uncertain",
+ "face-wink",
+ "face-worried"
+};
+
+static void
+html_editor_view_check_magic_links (EHTMLEditorView *view,
+ WebKitDOMRange *range,
+ gboolean include_space_by_user,
+ GdkEventKey *event)
+{
+ gchar *node_text;
+ gchar **urls;
+ GRegex *regex = NULL;
+ GMatchInfo *match_info;
+ gint start_pos_url, end_pos_url;
+ WebKitDOMNode *node;
+ gboolean include_space = FALSE;
+ gboolean return_pressed = FALSE;
+
+ if (event != NULL) {
+ if ((event->keyval == GDK_KEY_Return) ||
+ (event->keyval == GDK_KEY_Linefeed) ||
+ (event->keyval == GDK_KEY_KP_Enter)) {
+
+ return_pressed = TRUE;
+ }
+
+ if (event->keyval == GDK_KEY_space)
+ include_space = TRUE;
+ } else {
+ include_space = include_space_by_user;
+ }
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+
+ if (return_pressed)
+ node = webkit_dom_node_get_previous_sibling (node);
+
+ if (!node)
+ return;
+
+ if (!WEBKIT_DOM_IS_TEXT (node)) {
+ if (webkit_dom_node_has_child_nodes (node))
+ node = webkit_dom_node_get_first_child (node);
+ if (!WEBKIT_DOM_IS_TEXT (node))
+ return;
+ }
+
+ node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+ if (!node_text || !(*node_text) || !g_utf8_validate (node_text, -1, NULL))
+ return;
+
+ regex = g_regex_new (include_space ? URL_PATTERN_SPACE : URL_PATTERN, 0, 0, NULL);
+
+ if (!regex) {
+ g_free (node_text);
+ return;
+ }
+
+ g_regex_match_all (regex, node_text, G_REGEX_MATCH_NOTEMPTY, &match_info);
+ urls = g_match_info_fetch_all (match_info);
+
+ if (urls) {
+ gchar *final_url, *url_end_raw;
+ glong url_start, url_end, url_length;
+ WebKitDOMDocument *document;
+ WebKitDOMNode *url_text_node_clone;
+ WebKitDOMText *url_text_node;
+ WebKitDOMElement *anchor;
+ const gchar* url_text;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ if (!return_pressed)
+ e_html_editor_selection_save_caret_position (
+ e_html_editor_view_get_selection (view));
+
+ g_match_info_fetch_pos (match_info, 0, &start_pos_url, &end_pos_url);
+
+ /* Get start and end position of url in node's text because positions
+ * that we get from g_match_info_fetch_pos are not UTF-8 aware */
+ url_end_raw = g_strndup(node_text, end_pos_url);
+ url_end = g_utf8_strlen (url_end_raw, -1);
+
+ url_length = g_utf8_strlen (urls[0], -1);
+ url_start = url_end - url_length;
+
+ webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (node),
+ include_space ? url_end - 1 : url_end,
+ NULL);
+
+ url_text_node = webkit_dom_text_split_text (
+ WEBKIT_DOM_TEXT (node), url_start, NULL);
+ url_text_node_clone = webkit_dom_node_clone_node (
+ WEBKIT_DOM_NODE (url_text_node), TRUE);
+ url_text = webkit_dom_text_get_whole_text (
+ WEBKIT_DOM_TEXT (url_text_node_clone));
+
+ final_url = g_strconcat (
+ g_str_has_prefix (url_text, "www") ? "http://" : "", url_text, NULL);
+
+ /* Create and prepare new anchor element */
+ anchor = webkit_dom_document_create_element (document, "A", NULL);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (anchor),
+ url_text,
+ NULL);
+
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor),
+ final_url);
+
+ /* Insert new anchor element into document */
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (anchor),
+ WEBKIT_DOM_NODE (url_text_node),
+ NULL);
+
+ if (!return_pressed)
+ e_html_editor_selection_restore_caret_position (
+ e_html_editor_view_get_selection (view));
+
+ g_free (url_end_raw);
+ g_free (final_url);
+ } else {
+ WebKitDOMElement *parent;
+ WebKitDOMNode *prev_sibling;
+ gchar *href, *text, *url;
+ gint diff;
+ const char* text_to_append;
+ gboolean appending_to_link = FALSE;
+
+ parent = webkit_dom_node_get_parent_element (node);
+ prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+ /* If previous sibling is ANCHOR and actual text node is not beginning with
+ * space => we're appending to link */
+ if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) {
+ text_to_append = webkit_dom_node_get_text_content (node);
+ if (g_strcmp0 (text_to_append, "") != 0 &&
+ !g_unichar_isspace (g_utf8_get_char (text_to_append))) {
+
+ appending_to_link = TRUE;
+ parent = WEBKIT_DOM_ELEMENT (prev_sibling);
+ }
+ }
+
+ /* If parent is ANCHOR => we're editing the link */
+ if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && !appending_to_link) {
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+ g_free (node_text);
+ return;
+ }
+
+ /* edit only if href and description are the same */
+ href = webkit_dom_html_anchor_element_get_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent));
+
+ if (appending_to_link) {
+ gchar *inner_text;
+
+ inner_text =
+ webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (parent)),
+
+ text = g_strconcat (inner_text, text_to_append, NULL);
+ g_free (inner_text);
+ } else
+ text = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (parent));
+
+ if (strstr (href, "://") && !strstr (text, "://")) {
+ url = strstr (href, "://") + 3;
+ diff = strlen (text) - strlen (url);
+
+ if (text [strlen (text) - 1] != '/')
+ diff++;
+
+ if ((g_strcmp0 (url, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+ gchar *inner_html, *protocol, *new_href;
+
+ protocol = g_strndup (href, strstr (href, "://") - href + 3);
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (parent));
+ new_href = g_strconcat (
+ protocol, inner_html, appending_to_link ? text_to_append : "", NULL);
+
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+ new_href);
+
+ if (appending_to_link) {
+ gchar *tmp;
+
+ tmp = g_strconcat (inner_html, text_to_append, NULL);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (parent),
+ tmp,
+ NULL);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node, NULL);
+
+ g_free (tmp);
+ }
+
+ g_free (new_href);
+ g_free (protocol);
+ g_free (inner_html);
+ }
+ } else {
+ diff = strlen (text) - strlen (href);
+ if (text [strlen (text) - 1] != '/')
+ diff++;
+
+ if ((g_strcmp0 (href, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+ gchar *inner_html;
+ gchar *new_href;
+
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (parent));
+ new_href = g_strconcat (
+ inner_html,
+ appending_to_link ? text_to_append : "",
+ NULL);
+
+ webkit_dom_html_anchor_element_set_href (
+ WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+ new_href);
+
+ if (appending_to_link) {
+ gchar *tmp;
+
+ tmp = g_strconcat (inner_html, text_to_append, NULL);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (parent),
+ tmp,
+ NULL);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node, NULL);
+
+ g_free (tmp);
+ }
+
+ g_free (new_href);
+ g_free (inner_html);
+ }
+
+ }
+ g_free (text);
+ g_free (href);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+ g_free (node_text);
+}
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+ EHTMLEditorView *view;
+ gchar *content_type;
+ gchar *name;
+ EEmoticon *emoticon;
+};
+
+static LoadContext *
+emoticon_load_context_new (EHTMLEditorView *view,
+ EEmoticon *emoticon)
+{
+ LoadContext *load_context;
+
+ load_context = g_slice_new0 (LoadContext);
+ load_context->view = view;
+ load_context->emoticon = emoticon;
+
+ return load_context;
+}
+
+static void
+emoticon_load_context_free (LoadContext *load_context)
+{
+ g_free (load_context->content_type);
+ g_free (load_context->name);
+ g_slice_free (LoadContext, load_context);
+}
+
+static void
+emoticon_read_async_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ EHTMLEditorView *view = load_context->view;
+ EEmoticon *emoticon = load_context->emoticon;
+ GError *error = NULL;
+ gchar *html, *node_text = NULL, *mime_type;
+ gchar *base64_encoded, *output, *data;
+ const gchar *emoticon_start;
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ gssize size;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *span, *caret_position;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ input_stream = g_file_read_finish (file, result, &error);
+ g_return_if_fail (!error && input_stream);
+
+ output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+ size = g_output_stream_splice (
+ output_stream, G_INPUT_STREAM (input_stream),
+ G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error);
+
+ if (error || (size == -1))
+ goto out;
+
+ caret_position = e_html_editor_selection_save_caret_position (
+ e_html_editor_view_get_selection (view));
+
+ if (caret_position) {
+ WebKitDOMNode *parent;
+
+ parent = webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (caret_position));
+
+ /* Situation when caret is restored in body and not in paragraph */
+ if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ caret_position = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (parent),
+ WEBKIT_DOM_NODE (caret_position),
+ NULL));
+
+ caret_position = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (parent)),
+ WEBKIT_DOM_NODE (caret_position),
+ webkit_dom_node_get_first_child (
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (parent))),
+ NULL));
+ }
+ }
+
+ mime_type = g_content_type_get_mime_type (load_context->content_type);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ range = html_editor_view_get_dom_range (view);
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (WEBKIT_DOM_IS_TEXT (node))
+ node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+ span = webkit_dom_document_create_element (document, "SPAN", NULL);
+
+ data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream));
+
+ base64_encoded = g_base64_encode ((const guchar *) data, size);
+ output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+
+ /* Insert span with image representation and another one with text
+ * represetation and hide/show them dependant on active composer mode */
+ /* &#8203 == UNICODE_ZERO_WIDTH_SPACE */
+ html = g_strdup_printf (
+ "<span class=\"-x-evo-smiley-wrapper -x-evo-resizable-wrapper\">"
+ "<img src=\"%s\" alt=\"%s\" x-evo-smiley=\"%s\" "
+ "class=\"-x-evo-smiley-img\" data-inline data-name=\"%s\"/>"
+ "<span class=\"-x-evo-smiley-text\" style=\"display: none;\">%s"
+ "</span></span>&#8203;",
+ output, emoticon ? emoticon->text_face : "", emoticon->icon_name,
+ load_context->name, emoticon ? emoticon->text_face : "");
+
+ span = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (caret_position)),
+ WEBKIT_DOM_NODE (span),
+ WEBKIT_DOM_NODE (caret_position),
+ NULL));
+
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (span), html, NULL);
+
+ if (node_text) {
+ emoticon_start = g_utf8_strrchr (
+ node_text, -1, g_utf8_get_char (emoticon->text_face));
+ if (emoticon_start) {
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ g_utf8_strlen (node_text, -1) - strlen (emoticon_start),
+ strlen (emoticon->text_face),
+ NULL);
+ }
+ }
+
+ e_html_editor_selection_restore_caret_position (
+ e_html_editor_view_get_selection (view));
+
+ e_html_editor_view_set_changed (view, TRUE);
+
+ g_free (html);
+ g_free (node_text);
+ g_free (base64_encoded);
+ g_free (output);
+ g_free (mime_type);
+ g_object_unref (output_stream);
+ out:
+ emoticon_load_context_free (load_context);
+}
+
+static void
+emoticon_query_info_async_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GError *error = NULL;
+ GFileInfo *info;
+
+ info = g_file_query_info_finish (file, result, &error);
+ g_return_if_fail (!error && info);
+
+ load_context->content_type = g_strdup (g_file_info_get_content_type (info));
+ load_context->name = g_strdup (g_file_info_get_name (info));
+
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) emoticon_read_async_cb, load_context);
+
+ g_object_unref (info);
+}
+
+void
+e_html_editor_view_insert_smiley (EHTMLEditorView *view,
+ EEmoticon *emoticon)
+{
+ GFile *file;
+ gchar *filename_uri;
+ LoadContext *load_context;
+
+ filename_uri = e_emoticon_get_uri (emoticon);
+ g_return_if_fail (filename_uri != NULL);
+
+ load_context = emoticon_load_context_new (view, emoticon);
+
+ file = g_file_new_for_uri (filename_uri);
+ g_file_query_info_async (
+ file, "standard::*", G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context);
+
+ g_free (filename_uri);
+ g_object_unref (file);
+}
+
+static void
+html_editor_view_check_magic_smileys (EHTMLEditorView *view,
+ WebKitDOMRange *range)
+{
+ gint pos;
+ gint state;
+ gint relative;
+ gint start;
+ gchar *node_text;
+ gunichar uc;
+ WebKitDOMNode *node;
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+ if (!WEBKIT_DOM_IS_TEXT (node))
+ return;
+
+ node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+ if (node_text == NULL)
+ return;
+
+ start = webkit_dom_range_get_end_offset (range, NULL) - 1;
+ pos = start;
+ state = 0;
+ while (pos >= 0) {
+ uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos));
+ relative = 0;
+ while (emoticons_chars[state + relative]) {
+ if (emoticons_chars[state + relative] == uc)
+ break;
+ relative++;
+ }
+ state = emoticons_states[state + relative];
+ /* 0 .. not found, -n .. found n-th */
+ if (state <= 0)
+ break;
+ pos--;
+ }
+
+ /* Special case needed to recognize angel and devilish. */
+ if (pos > 0 && state == -14) {
+ uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+ if (uc == 'O') {
+ state = -1;
+ pos--;
+ } else if (uc == '>') {
+ state = -5;
+ pos--;
+ }
+ }
+
+ if (state < 0) {
+ const EEmoticon *emoticon;
+
+ if (pos > 0) {
+ uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+ if (!g_unichar_isspace (uc)) {
+ g_free (node_text);
+ return;
+ }
+ }
+
+ emoticon = (e_emoticon_chooser_lookup_emoticon (
+ emoticons_icon_names[-state - 1]));
+ e_html_editor_view_insert_smiley (view, (EEmoticon *) emoticon);
+ }
+
+ g_free (node_text);
+}
+
+static void
+html_editor_view_set_links_active (EHTMLEditorView *view,
+ gboolean active)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMElement *style;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ if (active) {
+ style = webkit_dom_document_get_element_by_id (
+ document, "--evolution-editor-style-a");
+ if (style) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (style)),
+ WEBKIT_DOM_NODE (style), NULL);
+ }
+ } else {
+ WebKitDOMHTMLHeadElement *head;
+ head = webkit_dom_document_get_head (document);
+
+ style = webkit_dom_document_create_element (document, "STYLE", NULL);
+ webkit_dom_element_set_id (style, "--evolution-editor-style-a");
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (style), "a { cursor: text; }", NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style), NULL);
+ }
+}
+
+static void
+clipboard_text_received (GtkClipboard *clipboard,
+ const gchar *text,
+ EHTMLEditorView *view)
+{
+ EHTMLEditorSelection *selection;
+ gchar *escaped_text;
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *dom_selection;
+ WebKitDOMElement *blockquote, *element;
+ WebKitDOMNode *node;
+ WebKitDOMRange *range;
+
+ if (!text || !*text)
+ return;
+
+ selection = e_html_editor_view_get_selection (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);
+
+ /* This is a trick to escape any HTML characters (like <, > or &).
+ * <textarea> automatically replaces all these unsafe characters
+ * by &lt;, &gt; etc. */
+ element = webkit_dom_document_create_element (document, "textarea", NULL);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element), text, NULL);
+ escaped_text = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element));
+
+ element = webkit_dom_document_create_element (document, "pre", NULL);
+
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (element), escaped_text, NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element),
+ e_html_editor_selection_get_caret_position_node (document),
+ NULL);
+
+ blockquote = webkit_dom_document_create_element (document, "blockquote", NULL);
+ webkit_dom_element_set_attribute (blockquote, "type", "cite", NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (blockquote), WEBKIT_DOM_NODE (element), NULL);
+
+ if (!e_html_editor_view_get_html_mode (view))
+ e_html_editor_view_quote_plain_text_element (view, element);
+
+ range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ node = webkit_dom_range_get_end_container (range, NULL);
+
+ webkit_dom_node_append_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (blockquote),
+ NULL);
+
+ e_html_editor_selection_restore_caret_position (selection);
+
+ e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+ g_free (escaped_text);
+}
+
+static void
+html_editor_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CHANGED:
+ e_html_editor_view_set_changed (
+ E_HTML_EDITOR_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_HTML_MODE:
+ e_html_editor_view_set_html_mode (
+ E_HTML_EDITOR_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_INLINE_SPELLING:
+ e_html_editor_view_set_inline_spelling (
+ E_HTML_EDITOR_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MAGIC_LINKS:
+ e_html_editor_view_set_magic_links (
+ E_HTML_EDITOR_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ e_html_editor_view_set_magic_smileys (
+ E_HTML_EDITOR_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CAN_COPY:
+ g_value_set_boolean (
+ value, webkit_web_view_can_copy_clipboard (
+ WEBKIT_WEB_VIEW (object)));
+ return;
+
+ case PROP_CAN_CUT:
+ g_value_set_boolean (
+ value, webkit_web_view_can_cut_clipboard (
+ WEBKIT_WEB_VIEW (object)));
+ return;
+
+ case PROP_CAN_PASTE:
+ g_value_set_boolean (
+ value, webkit_web_view_can_paste_clipboard (
+ WEBKIT_WEB_VIEW (object)));
+ return;
+
+ case PROP_CAN_REDO:
+ g_value_set_boolean (
+ value, webkit_web_view_can_redo (
+ WEBKIT_WEB_VIEW (object)));
+ return;
+
+ case PROP_CAN_UNDO:
+ g_value_set_boolean (
+ value, webkit_web_view_can_undo (
+ WEBKIT_WEB_VIEW (object)));
+ return;
+
+ case PROP_CHANGED:
+ g_value_set_boolean (
+ value, e_html_editor_view_get_changed (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+
+ case PROP_HTML_MODE:
+ g_value_set_boolean (
+ value, e_html_editor_view_get_html_mode (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+
+ case PROP_INLINE_SPELLING:
+ g_value_set_boolean (
+ value, e_html_editor_view_get_inline_spelling (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+
+ case PROP_MAGIC_LINKS:
+ g_value_set_boolean (
+ value, e_html_editor_view_get_magic_links (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ g_value_set_boolean (
+ value, e_html_editor_view_get_magic_smileys (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+
+ case PROP_SPELL_CHECKER:
+ g_value_set_object (
+ value, e_html_editor_view_get_spell_checker (
+ E_HTML_EDITOR_VIEW (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_view_dispose (GObject *object)
+{
+ EHTMLEditorViewPrivate *priv;
+
+ priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object);
+
+ g_clear_object (&priv->selection);
+
+ if (priv->convertor_web_view != NULL) {
+ g_object_unref (priv->convertor_web_view);
+ priv->convertor_web_view = NULL;
+ }
+
+ if (priv->aliasing_settings != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->aliasing_settings, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_unref (priv->aliasing_settings);
+ priv->aliasing_settings = NULL;
+ }
+
+ if (priv->font_settings != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->font_settings, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_unref (priv->font_settings);
+ priv->font_settings = NULL;
+ }
+
+ g_hash_table_remove_all (priv->inline_images);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_html_editor_view_parent_class)->dispose (object);
+}
+
+static void
+html_editor_view_finalize (GObject *object)
+{
+ EHTMLEditorViewPrivate *priv;
+
+ priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->inline_images);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_html_editor_view_parent_class)->finalize (object);
+}
+
+static void
+html_editor_view_constructed (GObject *object)
+{
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_html_editor_view_parent_class)->constructed (object);
+}
+
+static void
+html_editor_view_save_element_under_mouse_click (GtkWidget *widget)
+{
+ gint x, y;
+ GdkDeviceManager *device_manager;
+ GdkDevice *pointer;
+ EHTMLEditorView *view;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (widget));
+
+ device_manager = gdk_display_get_device_manager (
+ gtk_widget_get_display (GTK_WIDGET (widget)));
+ pointer = gdk_device_manager_get_client_pointer (device_manager);
+ gdk_window_get_device_position (
+ gtk_widget_get_window (GTK_WIDGET (widget)), pointer, &x, &y, NULL);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+ element = webkit_dom_document_element_from_point (document, x, y);
+
+ view = E_HTML_EDITOR_VIEW (widget);
+ view->priv->element_under_mouse = element;
+}
+
+static gboolean
+html_editor_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ gboolean event_handled;
+
+ if (event->button == 2) {
+ /* Middle click paste */
+ g_signal_emit (widget, signals[PASTE_PRIMARY_CLIPBOARD], 0);
+ event_handled = TRUE;
+ } else if (event->button == 3) {
+ html_editor_view_save_element_under_mouse_click (widget);
+ g_signal_emit (
+ widget, signals[POPUP_EVENT],
+ 0, event, &event_handled);
+ } else {
+ event_handled = FALSE;
+ }
+
+ if (event_handled)
+ return TRUE;
+
+ /* Chain up to parent's button_press_event() method. */
+ return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+ button_press_event (widget, event);
+}
+
+static gboolean
+html_editor_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ WebKitWebView *webview;
+ WebKitHitTestResult *hit_test;
+ WebKitHitTestResultContext context;
+ gchar *uri;
+
+ webview = WEBKIT_WEB_VIEW (widget);
+ hit_test = webkit_web_view_get_hit_test_result (webview, event);
+
+ g_object_get (
+ hit_test,
+ "context", &context,
+ "link-uri", &uri,
+ NULL);
+
+ g_object_unref (hit_test);
+
+ /* Left click on a link */
+ if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) &&
+ (event->button == 1)) {
+
+ /* Ctrl + Left Click on link opens it, otherwise ignore the
+ * click completely */
+ if (event->state & GDK_CONTROL_MASK) {
+ GtkWidget *toplevel;
+ GdkScreen *screen;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+ gtk_show_uri (screen, uri, event->time, NULL);
+ g_free (uri);
+ }
+
+ return TRUE;
+ }
+
+ g_free (uri);
+
+ /* Chain up to parent's button_release_event() method. */
+ return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+ button_release_event (widget, event);
+}
+
+static gboolean
+insert_new_line_into_citation (EHTMLEditorView *view)
+{
+ EHTMLEditorSelection *selection;
+ gboolean html_mode, ret_val;
+
+ html_mode = e_html_editor_view_get_html_mode (view);
+ selection = e_html_editor_view_get_selection (view);
+
+ ret_val = e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+ if (ret_val && !html_mode) {
+ WebKitDOMElement *element;
+ WebKitDOMDocument *document;
+ WebKitDOMNode *next_sibling;
+
+ document = webkit_web_view_get_dom_document (
+ WEBKIT_WEB_VIEW (view));
+
+ element = webkit_dom_document_query_selector (
+ document, "body>br", NULL);
+
+ next_sibling = webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (element));
+
+ if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_sibling)) {
+ /* Quote content */
+ next_sibling = WEBKIT_DOM_NODE (
+ e_html_editor_view_quote_plain_text_element (
+ view, WEBKIT_DOM_ELEMENT (next_sibling)));
+ /* Renew spellcheck */
+ e_html_editor_view_force_spell_check (view);
+ /* Insert caret node on right position */
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (element)),
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ WEBKIT_DOM_NODE (element),
+ NULL);
+ /* Restore caret position */
+ e_html_editor_selection_restore_caret_position (
+ selection);
+ }
+ }
+
+ return ret_val;
+}
+
+static gboolean
+prevent_from_deleting_last_element_in_body (EHTMLEditorView *view)
+{
+ gboolean ret_val = FALSE;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ list = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (body));
+
+ if (webkit_dom_node_list_get_length (list) <= 1) {
+ gchar *content;
+
+ content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body));
+
+ if (!*content)
+ ret_val = TRUE;
+
+ g_free (content);
+
+ if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body), "img", NULL))
+ ret_val = FALSE;
+ }
+
+ return ret_val;
+}
+
+static gboolean
+html_editor_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EHTMLEditorView *view = E_HTML_EDITOR_VIEW (widget);
+
+ if (event->keyval == GDK_KEY_Tab)
+ return e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+
+ if ((event->keyval == GDK_KEY_Control_L) ||
+ (event->keyval == GDK_KEY_Control_R)) {
+
+ html_editor_view_set_links_active (view, TRUE);
+ }
+
+ if ((event->keyval == GDK_KEY_Return) ||
+ (event->keyval == GDK_KEY_KP_Enter)) {
+ EHTMLEditorSelection *selection;
+
+ selection = e_html_editor_view_get_selection (view);
+ /* When user presses ENTER in a citation block, WebKit does
+ * not break the citation automatically, so we need to use
+ * the special command to do it. */
+ if (e_html_editor_selection_is_citation (selection))
+ return insert_new_line_into_citation (view);
+ }
+
+ /* BackSpace in indented block decrease indent level by one */
+ if (event->keyval == GDK_KEY_BackSpace) {
+ EHTMLEditorSelection *selection;
+
+ selection = e_html_editor_view_get_selection (view);
+ if (e_html_editor_selection_is_indented (selection)) {
+ WebKitDOMElement *caret;
+
+ caret = e_html_editor_selection_save_caret_position (selection);
+
+ if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (caret))) {
+ e_html_editor_selection_clear_caret_position_marker (selection);
+ e_html_editor_selection_unindent (selection);
+ return TRUE;
+ } else
+ e_html_editor_selection_clear_caret_position_marker (selection);
+ }
+
+ if (prevent_from_deleting_last_element_in_body (view))
+ return TRUE;
+ }
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+ key_press_event (widget, event);
+}
+
+static void
+mark_node_as_paragraph_after_ending_list (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document)
+{
+ gint ii, length;
+ WebKitDOMNodeList *list;
+
+ /* When pressing Enter on empty line in the list WebKit will end that
+ * list and inserts <div><br></div> so mark it for wrapping */
+ list = webkit_dom_document_query_selector_all (
+ document, "body > div:not(.-x-evo-paragraph) > br", NULL);
+
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_get_parent_node (
+ webkit_dom_node_list_item (list, ii));
+
+ e_html_editor_selection_set_paragraph_style (
+ selection, WEBKIT_DOM_ELEMENT (node), -1, 0, "");
+ }
+}
+
+static gboolean
+surround_text_with_paragraph_if_needed (EHTMLEditorSelection *selection,
+ WebKitDOMDocument *document,
+ WebKitDOMNode *node)
+{
+ WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node);
+ WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (node);
+ WebKitDOMElement *element;
+
+ /* All text in composer has to be written in div elements, so if
+ * we are writing something straight to the body, surround it with
+ * paragraph */
+ if (WEBKIT_DOM_IS_TEXT (node) &&
+ WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) {
+ element = e_html_editor_selection_put_node_into_paragraph (
+ selection,
+ document,
+ node,
+ e_html_editor_selection_get_caret_position_node (document));
+
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (next_sibling),
+ next_sibling,
+ NULL);
+ }
+
+ /* Tab character */
+ if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Apple-tab-span")) {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (element),
+ prev_sibling,
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (element)),
+ NULL);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+html_editor_view_key_release_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+
+ view = E_HTML_EDITOR_VIEW (widget);
+ range = html_editor_view_get_dom_range (view);
+ selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+ if (view->priv->magic_smileys &&
+ view->priv->html_mode) {
+ html_editor_view_check_magic_smileys (view, range);
+ }
+
+ if ((event->keyval == GDK_KEY_Return) ||
+ (event->keyval == GDK_KEY_Linefeed) ||
+ (event->keyval == GDK_KEY_KP_Enter) ||
+ (event->keyval == GDK_KEY_space)) {
+
+ html_editor_view_check_magic_links (view, range, FALSE, event);
+
+ mark_node_as_paragraph_after_ending_list (selection, document);
+ } else {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_range_get_end_container (range, NULL);
+
+ if (surround_text_with_paragraph_if_needed (selection, document, node)) {
+ e_html_editor_selection_restore_caret_position (selection);
+ node = webkit_dom_range_get_end_container (range, NULL);
+ range = html_editor_view_get_dom_range (view);
+ }
+
+ if (WEBKIT_DOM_IS_TEXT (node)) {
+ gchar *text;
+
+ text = webkit_dom_node_get_text_content (node);
+
+ if (g_strcmp0 (text, "") != 0 && !g_unichar_isspace (g_utf8_get_char (text))) {
+ WebKitDOMNode *prev_sibling;
+
+ prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+ if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling))
+ html_editor_view_check_magic_links (view, range, FALSE, event);
+ }
+ g_free (text);
+ }
+ }
+
+ if ((event->keyval == GDK_KEY_Control_L) ||
+ (event->keyval == GDK_KEY_Control_R)) {
+
+ html_editor_view_set_links_active (view, FALSE);
+ }
+
+ /* Chain up to parent's key_release_event() method. */
+ return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+ key_release_event (widget, event);
+}
+
+static void
+html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_clipboard_get_for_display (
+ gdk_display_get_default (),
+ GDK_SELECTION_CLIPBOARD);
+
+ gtk_clipboard_request_text (
+ clipboard,
+ (GtkClipboardTextReceivedFunc) clipboard_text_received,
+ view);
+}
+
+static gboolean
+html_editor_view_image_exists_in_cache (const gchar *image_uri)
+{
+ gchar *filename;
+ gchar *hash;
+ gboolean exists = FALSE;
+
+ g_return_val_if_fail (emd_global_http_cache != NULL, FALSE);
+
+ hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
+ filename = camel_data_cache_get_filename (
+ emd_global_http_cache, "http", hash);
+
+ if (filename != NULL) {
+ exists = g_file_test (filename, G_FILE_TEST_EXISTS);
+ g_free (filename);
+ }
+
+ g_free (hash);
+
+ return exists;
+}
+
+static gchar *
+html_editor_view_redirect_uri (EHTMLEditorView *view,
+ const gchar *uri)
+{
+ EImageLoadingPolicy image_policy;
+ GSettings *settings;
+ gboolean uri_is_http;
+
+ uri_is_http =
+ g_str_has_prefix (uri, "http:") ||
+ g_str_has_prefix (uri, "https:") ||
+ g_str_has_prefix (uri, "evo-http:") ||
+ g_str_has_prefix (uri, "evo-https:");
+
+ /* Redirect http(s) request to evo-http(s) protocol.
+ * See EMailRequest for further details about this. */
+ if (uri_is_http) {
+ gchar *new_uri;
+ SoupURI *soup_uri;
+ gboolean image_exists;
+
+ /* Check Evolution's cache */
+ image_exists = html_editor_view_image_exists_in_cache (uri);
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ image_policy = g_settings_get_enum (settings, "image-loading-policy");
+ g_object_unref (settings);
+ /* If the URI is not cached and we are not allowed to load it
+ * then redirect to invalid URI, so that webkit would display
+ * a native placeholder for it. */
+ if (!image_exists && (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
+ return g_strdup ("about:blank");
+ }
+
+ new_uri = g_strconcat ("evo-", uri, NULL);
+ soup_uri = soup_uri_new (new_uri);
+ g_free (new_uri);
+
+ new_uri = soup_uri_to_string (soup_uri, FALSE);
+
+ soup_uri_free (soup_uri);
+
+ return new_uri;
+ }
+
+ return g_strdup (uri);
+}
+
+static void
+html_editor_view_resource_requested (WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ WebKitWebResource *resource,
+ WebKitNetworkRequest *request,
+ WebKitNetworkResponse *response,
+ gpointer user_data)
+{
+ const gchar *original_uri;
+
+ original_uri = webkit_network_request_get_uri (request);
+
+ if (original_uri != NULL) {
+ gchar *redirected_uri;
+
+ redirected_uri = html_editor_view_redirect_uri (
+ E_HTML_EDITOR_VIEW (web_view), original_uri);
+
+ webkit_network_request_set_uri (request, redirected_uri);
+
+ g_free (redirected_uri);
+ }
+}
+
+static void
+e_html_editor_view_class_init (EHTMLEditorViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = html_editor_view_get_property;
+ object_class->set_property = html_editor_view_set_property;
+ object_class->dispose = html_editor_view_dispose;
+ object_class->finalize = html_editor_view_finalize;
+ object_class->constructed = html_editor_view_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->button_press_event = html_editor_view_button_press_event;
+ widget_class->button_release_event = html_editor_view_button_release_event;
+ widget_class->key_press_event = html_editor_view_key_press_event;
+ widget_class->key_release_event = html_editor_view_key_release_event;
+
+ class->paste_clipboard_quoted = html_editor_view_paste_clipboard_quoted;
+
+ /**
+ * EHTMLEditorView:can-copy
+ *
+ * Determines whether it's possible to copy to clipboard. The action
+ * is usually disabled when there is no selection to copy.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CAN_COPY,
+ g_param_spec_boolean (
+ "can-copy",
+ "Can Copy",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:can-cut
+ *
+ * Determines whether it's possible to cut to clipboard. The action
+ * is usually disabled when there is no selection to cut.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CAN_CUT,
+ g_param_spec_boolean (
+ "can-cut",
+ "Can Cut",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:can-paste
+ *
+ * Determines whether it's possible to paste from clipboard. The action
+ * is usually disabled when there is no valid content in clipboard to
+ * paste.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CAN_PASTE,
+ g_param_spec_boolean (
+ "can-paste",
+ "Can Paste",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:can-redo
+ *
+ * Determines whether it's possible to redo previous action. The action
+ * is usually disabled when there is no action to redo.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CAN_REDO,
+ g_param_spec_boolean (
+ "can-redo",
+ "Can Redo",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:can-undo
+ *
+ * Determines whether it's possible to undo last action. The action
+ * is usually disabled when there is no previous action to undo.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CAN_UNDO,
+ g_param_spec_boolean (
+ "can-undo",
+ "Can Undo",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:changed
+ *
+ * Determines whether document has been modified
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_CHANGED,
+ g_param_spec_boolean (
+ "changed",
+ _("Changed property"),
+ _("Whether editor changed"),
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:html-mode
+ *
+ * Determines whether HTML or plain text mode is enabled.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_HTML_MODE,
+ g_param_spec_boolean (
+ "html-mode",
+ "HTML Mode",
+ "Edit HTML or plain text",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView::inline-spelling
+ *
+ * Determines whether automatic spellchecking is enabled.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_INLINE_SPELLING,
+ g_param_spec_boolean (
+ "inline-spelling",
+ "Inline Spelling",
+ "Check your spelling as you type",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:magic-links
+ *
+ * Determines whether automatic conversion of text links into
+ * HTML links is enabled.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_LINKS,
+ g_param_spec_boolean (
+ "magic-links",
+ "Magic Links",
+ "Make URIs clickable as you type",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:magic-smileys
+ *
+ * Determines whether automatic conversion of text smileys into
+ * images is enabled.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_SMILEYS,
+ g_param_spec_boolean (
+ "magic-smileys",
+ "Magic Smileys",
+ "Convert emoticons to images as you type",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:spell-checker:
+ *
+ * The #ESpellChecker used for spell checking.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_SPELL_CHECKER,
+ g_param_spec_object (
+ "spell-checker",
+ "Spell Checker",
+ "The spell checker",
+ E_TYPE_SPELL_CHECKER,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EHTMLEditorView:popup-event
+ *
+ * Emitted whenever a context menu is requested.
+ */
+ signals[POPUP_EVENT] = g_signal_new (
+ "popup-event",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EHTMLEditorViewClass, popup_event),
+ g_signal_accumulator_true_handled, NULL,
+ e_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+ /**
+ * EHTMLEditorView:paste-primary-clipboad
+ *
+ * Emitted when user presses middle button on EHTMLEditorView
+ */
+ signals[PASTE_PRIMARY_CLIPBOARD] = g_signal_new (
+ "paste-primary-clipboard",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EHTMLEditorViewClass, paste_primary_clipboard),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static GString *
+replace_string_spaces_with_nbsp_in_prefix (const gchar *text)
+{
+ GString *str;
+ gint counter = 0;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ str = g_string_new ("");
+
+ while (g_str_has_prefix (text + counter, " ")) {
+ g_string_append (str, "&nbsp;");
+
+ counter++;
+ }
+
+ g_string_append (str, text + counter);
+
+ return str;
+}
+
+/* This parses the HTML code (that contains just text, &nbsp; and BR elements)
+ * into paragraphs.
+ * HTML code in that format we can get by taking innerText from some element,
+ * setting it to another one and finally getting innerHTML from it */
+static void
+parse_html_into_paragraphs (EHTMLEditorView *view,
+ WebKitDOMDocument *document,
+ WebKitDOMElement *blockquote,
+ const gchar *html,
+ gboolean use_pre)
+{
+ const gchar *prev_br, *next_br;
+ gchar *inner_html;
+ gint citation_level = 0;
+ GString *start, *end;
+ gboolean ignore_next_br = FALSE;
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (blockquote), "", NULL);
+
+ prev_br = html;
+ next_br = strstr (prev_br, "<br>");
+
+ while (next_br) {
+ gboolean local_ignore_next_br = ignore_next_br;
+ const gchar *citation = NULL, *citation_end = NULL;
+ const gchar *rest = NULL, *with_br = NULL;
+ gchar *to_insert = NULL;
+ WebKitDOMElement *paragraph;
+
+ to_insert = g_utf8_substring (
+ prev_br, 0, g_utf8_pointer_to_offset (prev_br, next_br));
+
+ with_br = strstr (to_insert, "<br>");
+
+ ignore_next_br = FALSE;
+
+ citation = strstr (to_insert, "##CITATION_");
+ if (citation) {
+ if (strstr (to_insert, "##CITATION_START##"))
+ citation_level++;
+ else
+ citation_level--;
+
+ citation_end = strstr (citation + 2, "##");
+ if (citation_end)
+ rest = citation_end + 2;
+ } else {
+ rest = with_br ?
+ to_insert + 4 + (with_br - to_insert) : to_insert;
+ }
+
+ if (use_pre) {
+ paragraph = webkit_dom_document_create_element (
+ document, "pre", NULL);
+ } else {
+ paragraph = e_html_editor_selection_get_paragraph_element (
+ e_html_editor_view_get_selection (view),
+ document, -1, citation_level);
+ }
+
+ if (with_br && !*rest && !citation &&!local_ignore_next_br) {
+ WebKitDOMNode *paragraph_clone;
+
+ paragraph_clone = webkit_dom_node_clone_node (
+ WEBKIT_DOM_NODE (paragraph), TRUE);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (paragraph_clone),
+ "&nbsp;",
+ NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (blockquote),
+ paragraph_clone,
+ NULL);
+ }
+
+ if (citation) {
+ WebKitDOMText *text;
+ gchar *citation_mark;
+
+ citation_mark = g_utf8_substring (
+ citation, 0,
+ g_utf8_pointer_to_offset (
+ citation, citation_end + 2));
+
+ text = webkit_dom_document_create_text_node (
+ document, citation_mark);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (blockquote),
+ WEBKIT_DOM_NODE (text),
+ NULL);
+
+ g_free (citation_mark);
+ }
+
+ if (rest && *rest){
+ GString *space_to_nbsp;
+
+ space_to_nbsp = replace_string_spaces_with_nbsp_in_prefix (rest);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (paragraph),
+ space_to_nbsp->str,
+ NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (blockquote),
+ WEBKIT_DOM_NODE (paragraph),
+ NULL);
+ g_string_free (space_to_nbsp, TRUE);
+ }
+
+ if (citation_end)
+ ignore_next_br = TRUE;
+
+ prev_br = next_br;
+ next_br = strstr (prev_br + 4, "<br>");
+ g_free (to_insert);
+ }
+
+ if (g_utf8_strlen (prev_br, -1) > 0) {
+ WebKitDOMElement *paragraph;
+
+ if (use_pre) {
+ paragraph = webkit_dom_document_create_element (
+ document, "pre", NULL);
+ } else {
+ paragraph = e_html_editor_selection_get_paragraph_element (
+ e_html_editor_view_get_selection (view),
+ document, -1, citation_level);
+ }
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (paragraph),
+ g_str_has_prefix (prev_br, "<br>") ? prev_br + 4 : prev_br,
+ NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (blockquote),
+ WEBKIT_DOM_NODE (paragraph),
+ NULL);
+ }
+
+ /* Replace text markers with actual HTML blockquotes */
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (blockquote));
+ start = e_str_replace_string (
+ inner_html, "##CITATION_START##","<blockquote type=\"cite\">");
+ end = e_str_replace_string (
+ start->str, "##CITATION_END##", "</blockquote>");
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (blockquote), end->str, NULL);
+
+ g_free (inner_html);
+ g_string_free (start, TRUE);
+ g_string_free (end, TRUE);
+}
+
+static void
+mark_citation (WebKitDOMElement *citation)
+{
+ gchar *inner_html, *surrounded;
+
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (citation));
+
+ surrounded = g_strconcat (
+ "<span>##CITATION_START##</span>", inner_html,
+ "<span>##CITATION_END##</span>", NULL);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (citation), surrounded, NULL);
+
+ element_add_class (citation, "marked");
+
+ g_free (inner_html);
+ g_free (surrounded);
+}
+
+static gint
+create_text_markers_for_citations_in_document (WebKitDOMDocument *document)
+{
+ gint count = 0;
+ WebKitDOMElement *citation;
+
+ citation = webkit_dom_document_query_selector (
+ document, "blockquote[type=cite]:not(.marked)", NULL);
+
+ while (citation) {
+ mark_citation (citation);
+ count ++;
+
+ citation = webkit_dom_document_query_selector (
+ document, "blockquote[type=cite]:not(.marked)", NULL);
+ }
+
+ return count;
+}
+
+static gint
+create_text_markers_for_citations_in_element (WebKitDOMElement *element)
+{
+ gint count = 0;
+ WebKitDOMElement *citation;
+
+ citation = webkit_dom_element_query_selector (
+ element, "blockquote[type=cite]:not(.marked)", NULL);
+
+ while (citation) {
+ mark_citation (citation);
+ count ++;
+
+ citation = webkit_dom_element_query_selector (
+ element, "blockquote[type=cite]:not(.marked)", NULL);
+ }
+
+ return count;
+}
+
+static void
+html_editor_view_process_document_from_convertor (EHTMLEditorView *view,
+ WebKitDOMDocument *document_convertor)
+{
+ EHTMLEditorSelection *selection = e_html_editor_view_get_selection (view);
+ gboolean start_bottom;
+ gchar *inner_text, *inner_html;
+ gint ii;
+ GSettings *settings;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *paragraph, *new_blockquote, *top_signature;
+ WebKitDOMElement *cite_body, *signature;
+ WebKitDOMHTMLElement *body, *body_convertor;
+ WebKitDOMNodeList *list;
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+ g_object_unref (settings);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+ body_convertor = webkit_dom_document_get_body (document_convertor);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL);
+
+ paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-input-start");
+ if (!paragraph) {
+ paragraph = e_html_editor_selection_get_paragraph_element (
+ selection, document, -1, 0);
+ webkit_dom_element_set_id (paragraph, "-x-evo-input-start");
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (paragraph), UNICODE_ZERO_WIDTH_SPACE, NULL);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)),
+ WEBKIT_DOM_NODE (paragraph),
+ NULL);
+ }
+
+ list = webkit_dom_document_query_selector_all (
+ document_convertor, "span.-x-evo-to-body", NULL);
+ for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_node_list_item (list, ii);
+ while (webkit_dom_node_has_child_nodes (node)) {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ webkit_dom_node_clone_node (
+ webkit_dom_node_get_first_child (node), TRUE),
+ webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (paragraph)),
+ NULL);
+
+ webkit_dom_node_remove_child (
+ node, webkit_dom_node_get_first_child (node), NULL);
+ }
+
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (body_convertor),
+ WEBKIT_DOM_NODE (node),
+ NULL);
+ }
+
+ repair_gmail_blockquotes (document_convertor);
+
+ create_text_markers_for_citations_in_document (document_convertor);
+
+ /* Get innertText from convertor */
+ inner_text = webkit_dom_html_element_get_inner_text (body_convertor);
+
+ cite_body = webkit_dom_document_query_selector (
+ document_convertor, "span.-x-evo-cite-body", NULL);
+
+ top_signature = webkit_dom_document_query_selector (
+ document, ".-x-evo-top-signature", NULL);
+ signature = webkit_dom_document_query_selector (
+ document, "span.-x-evo-signature", NULL);
+
+ if (cite_body) {
+ if (!(top_signature && start_bottom))
+ e_html_editor_selection_save_caret_position (selection);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (paragraph),
+ WEBKIT_DOM_NODE (
+ e_html_editor_selection_get_caret_position_node (
+ document)),
+ NULL);
+ }
+
+ new_blockquote = webkit_dom_document_create_element (
+ document, "blockquote", NULL);
+ webkit_dom_element_set_attribute (
+ new_blockquote, "type", "cite", NULL);
+
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (new_blockquote), inner_text, NULL);
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (new_blockquote));
+
+ if (cite_body) {
+ webkit_dom_element_set_attribute (
+ new_blockquote, "id", "-x-evo-main-cite", NULL);
+
+ parse_html_into_paragraphs (
+ view, document, new_blockquote, inner_html, FALSE);
+
+ if (start_bottom) {
+ if (signature) {
+ WebKitDOMNode *parent =
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature));
+ if (top_signature) {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ NULL);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (paragraph),
+ NULL);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (paragraph),
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ NULL);
+ } else {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ WEBKIT_DOM_NODE (parent),
+ NULL);
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (paragraph),
+ webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (new_blockquote)),
+ NULL);
+ }
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ NULL);
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (paragraph),
+ webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (new_blockquote)),
+ NULL);
+ }
+ } else {
+ if (signature) {
+ WebKitDOMNode *parent =
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature));
+
+ if (top_signature) {
+ WebKitDOMElement *br;
+
+ br = webkit_dom_document_create_element (
+ document, "BR", NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ NULL);
+ /* Insert NL after signature */
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (br),
+ webkit_dom_node_get_next_sibling (
+ WEBKIT_DOM_NODE (paragraph)),
+ NULL);
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (parent),
+ WEBKIT_DOM_NODE (br),
+ NULL);
+ } else
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ WEBKIT_DOM_NODE (parent),
+ NULL);
+ } else {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (new_blockquote),
+ NULL);
+ }
+ }
+ } else {
+ WebKitDOMNode *signature_clone, *first_child;
+
+ if (signature) {
+ signature_clone = webkit_dom_node_clone_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature)),
+ TRUE);
+ }
+
+ parse_html_into_paragraphs (
+ view, document, WEBKIT_DOM_ELEMENT (body),
+ inner_html, FALSE);
+
+ if (signature) {
+ if (!top_signature) {
+ signature_clone = webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ signature_clone,
+ NULL);
+ } else {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (body),
+ signature_clone,
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (body)),
+ NULL);
+ }
+ }
+
+ first_child = webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (body));
+
+ webkit_dom_node_insert_before (
+ first_child,
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ webkit_dom_node_get_first_child (first_child),
+ NULL);
+ }
+
+ if (!e_html_editor_view_get_html_mode (view))
+ e_html_editor_selection_wrap_paragraphs_in_document (
+ selection, document);
+ if (webkit_dom_document_query_selector (document, "blockquote[type=cite]", NULL))
+ body = WEBKIT_DOM_HTML_ELEMENT (
+ e_html_editor_view_quote_plain_text (view));
+
+ e_html_editor_selection_restore_caret_position (selection);
+ e_html_editor_view_force_spell_check (view);
+
+ /* Register on input event that is called when the content (body) is modified */
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (body),
+ "input",
+ G_CALLBACK (body_input_event_cb),
+ FALSE,
+ view);
+
+ g_free (inner_html);
+ g_free (inner_text);
+}
+
+static void
+html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view,
+ WebKitDOMDocument *document_convertor)
+{
+ gchar *inner_text, *inner_html;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+ WebKitDOMHTMLElement *convertor_body;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ convertor_body = webkit_dom_document_get_body (document_convertor);
+
+ inner_text = webkit_dom_html_element_get_inner_text (convertor_body);
+ element = webkit_dom_document_create_element (document, "div", NULL);
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (element), inner_text, NULL);
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element));
+
+ parse_html_into_paragraphs (
+ view, document, element, inner_html, FALSE);
+
+ g_free (inner_html);
+
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element));
+
+ e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html);
+
+ e_html_editor_view_force_spell_check (view);
+
+ g_free (inner_html);
+ g_free (inner_text);
+}
+
+static void
+html_plain_text_convertor_load_status_changed (WebKitWebView *web_view,
+ GParamSpec *pspec,
+ EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document_convertor;
+
+ if (webkit_web_view_get_load_status (web_view) != WEBKIT_LOAD_FINISHED)
+ return;
+
+ document_convertor = webkit_web_view_get_dom_document (web_view);
+
+ if (view->priv->convertor_insert)
+ html_editor_view_insert_converted_html_into_selection (
+ view, document_convertor);
+ else
+ html_editor_view_process_document_from_convertor (
+ view, document_convertor);
+}
+
+static void
+e_html_editor_view_init (EHTMLEditorView *view)
+{
+ WebKitWebSettings *settings;
+ GSettings *g_settings;
+ GSettingsSchema *settings_schema;
+ ESpellChecker *checker;
+ gchar **languages;
+ gchar *comma_separated;
+ const gchar *user_cache_dir;
+
+ view->priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (view);
+
+ webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), TRUE);
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+
+ g_object_set (
+ G_OBJECT (settings),
+ "enable-developer-extras", TRUE,
+ "enable-dom-paste", TRUE,
+ "enable-file-access-from-file-uris", TRUE,
+ "enable-plugins", FALSE,
+ "enable-scripts", FALSE,
+ "enable-spell-checking", TRUE,
+ "respect-image-orientation", TRUE,
+ NULL);
+
+ webkit_web_view_set_settings (WEBKIT_WEB_VIEW (view), settings);
+
+ /* Override the spell-checker, use our own */
+ checker = e_spell_checker_new ();
+ webkit_set_text_checker (G_OBJECT (checker));
+ g_object_unref (checker);
+
+ /* Don't use CSS when possible to preserve compatibility with older
+ * versions of Evolution or other MUAs */
+ e_html_editor_view_exec_command (
+ view, E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "false");
+
+ g_signal_connect (
+ view, "user-changed-contents",
+ G_CALLBACK (html_editor_view_user_changed_contents_cb), NULL);
+ g_signal_connect (
+ view, "selection-changed",
+ G_CALLBACK (html_editor_view_selection_changed_cb), NULL);
+ g_signal_connect (
+ view, "should-show-delete-interface-for-element",
+ G_CALLBACK (html_editor_view_should_show_delete_interface_for_element), NULL);
+ g_signal_connect (
+ view, "resource-request-starting",
+ G_CALLBACK (html_editor_view_resource_requested), NULL);
+ g_signal_connect (
+ view, "notify::load-status",
+ G_CALLBACK (html_editor_view_load_status_changed), NULL);
+
+ view->priv->selection = g_object_new (
+ E_TYPE_HTML_EDITOR_SELECTION,
+ "html-editor-view", view,
+ NULL);
+
+ g_settings = g_settings_new ("org.gnome.desktop.interface");
+ g_signal_connect_swapped (
+ g_settings, "changed::font-name",
+ G_CALLBACK (e_html_editor_view_update_fonts), view);
+ g_signal_connect_swapped (
+ g_settings, "changed::monospace-font-name",
+ G_CALLBACK (e_html_editor_view_update_fonts), view);
+ view->priv->font_settings = g_settings;
+
+ /* This schema is optional. Use if available. */
+ settings_schema = g_settings_schema_source_lookup (
+ g_settings_schema_source_get_default (),
+ "org.gnome.settings-daemon.plugins.xsettings", FALSE);
+ if (settings_schema != NULL) {
+ g_settings = g_settings_new ("org.gnome.settings-daemon.plugins.xsettings");
+ g_signal_connect_swapped (
+ settings, "changed::antialiasing",
+ G_CALLBACK (e_html_editor_view_update_fonts), view);
+ view->priv->aliasing_settings = g_settings;
+ }
+
+ view->priv->inline_images = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ e_html_editor_view_update_fonts (view);
+
+ /* Give spell check languages to WebKit */
+ languages = e_spell_checker_list_active_languages (checker, NULL);
+ comma_separated = g_strjoinv (",", languages);
+ g_strfreev (languages);
+
+ g_object_set (
+ G_OBJECT (settings),
+ "spell-checking-languages", comma_separated,
+ NULL);
+
+ g_free (comma_separated);
+
+ view->priv->convertor_insert = FALSE;
+
+ view->priv->convertor_web_view =
+ g_object_ref_sink (WEBKIT_WEB_VIEW (webkit_web_view_new ()));
+ settings = webkit_web_view_get_settings (view->priv->convertor_web_view);
+
+ g_object_set (
+ G_OBJECT (settings),
+ "enable-scripts", FALSE,
+ "enable-plugins", FALSE,
+ NULL);
+
+ g_signal_connect (
+ view->priv->convertor_web_view, "notify::load-status",
+ G_CALLBACK (html_plain_text_convertor_load_status_changed), view);
+
+ /* Make WebKit think we are displaying a local file, so that it
+ * does not block loading resources from file:// protocol */
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (view), "", "text/html", "UTF-8", "file://");
+
+ html_editor_view_set_links_active (view, FALSE);
+
+ if (emd_global_http_cache == NULL) {
+ user_cache_dir = e_get_user_cache_dir ();
+ emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
+
+ /* cache expiry - 2 hour access, 1 day max */
+ camel_data_cache_set_expire_age (
+ emd_global_http_cache, 24 * 60 * 60);
+ camel_data_cache_set_expire_access (
+ emd_global_http_cache, 2 * 60 * 60);
+ }
+}
+
+/**
+ * e_html_editor_view_new:
+ *
+ * Returns a new instance of the editor.
+ *
+ * Returns: A newly created #EHTMLEditorView. [transfer-full]
+ */
+EHTMLEditorView *
+e_html_editor_view_new (void)
+{
+ return g_object_new (E_TYPE_HTML_EDITOR_VIEW, NULL);
+}
+
+/**
+ * e_html_editor_view_get_selection:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns an #EHTMLEditorSelection object which represents current selection or
+ * cursor position within the editor document. The #EHTMLEditorSelection allows
+ * programmer to manipulate with formatting, selection, styles etc.
+ *
+ * Returns: An always valid #EHTMLEditorSelection object. The object is owned by
+ * the @view and should never be free'd.
+ */
+EHTMLEditorSelection *
+e_html_editor_view_get_selection (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+
+ return view->priv->selection;
+}
+
+/**
+ * e_html_editor_view_exec_command:
+ * @view: an #EHTMLEditorView
+ * @command: an #EHTMLEditorViewCommand to execute
+ * @value: value of the command (or @NULL if the command does not require value)
+ *
+ * The function will fail when @value is @NULL or empty but the current @command
+ * requires a value to be passed. The @value is ignored when the @command does
+ * not expect any value.
+ *
+ * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_exec_command (EHTMLEditorView *view,
+ EHTMLEditorViewCommand command,
+ const gchar *value)
+{
+ WebKitDOMDocument *document;
+ const gchar *cmd_str = 0;
+ gboolean has_value;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+#define CHECK_COMMAND(cmd,str,val) case cmd:\
+ if (val) {\
+ g_return_val_if_fail (value && *value, FALSE);\
+ }\
+ has_value = val; \
+ cmd_str = str;\
+ break;
+
+ switch (command) {
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, "BackColor", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BOLD, "Bold", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_COPY, "Copy", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, "CreateLink", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CUT, "Cut", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, "DefaultParagraphSeparator", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DELETE, "Delete", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING, "FindString", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, "FontName", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, "FontSize", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA, "FontSizeDelta", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, "ForeColor", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK, "FormatBlock", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, "ForwardDelete", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR, "HiliteColor", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INDENT, "Indent", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE, "InsertHorizontalRule", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, "InsertHTML", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE, "InsertImage", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK, "InsertLineBreak", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, "InsertNewlineInQuotedContent", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST, "InsertOrderedList", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH, "InsertParagraph", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "InsertText", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST, "InsertUnorderedList", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_ITALIC, "Italic", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER, "JustifyCenter", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL, "JustifyFull", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT, "JustifyLeft", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE, "JustifyNone", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT, "JustifyRight", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_OUTDENT, "Outdent", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE, "Paste", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE, "PasteAndMatchStyle", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT, "PasteAsPlainText", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PRINT, "Print", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REDO, "Redo", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT, "RemoveFormat", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL, "SelectAll", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, "Strikethrough", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "StyleWithCSS", TRUE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, "Subscript", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, "Superscript", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE, "Transpose", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, "Underline", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDO, "Undo", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNLINK, "Unlink", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNSELECT, "Unselect", FALSE)
+ CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_USE_CSS, "UseCSS", TRUE)
+ }
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ return webkit_dom_document_exec_command (
+ document, cmd_str, FALSE, has_value ? value : "" );
+}
+
+/**
+ * e_html_editor_view_get_changed:
+ * @view: an #EHTMLEditorView
+ *
+ * Whether content of the editor has been changed.
+ *
+ * Returns: @TRUE when document was changed, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_changed (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+ return view->priv->changed;
+}
+
+/**
+ * e_html_editor_view_set_changed:
+ * @view: an #EHTMLEditorView
+ * @changed: whether document has been changed or not
+ *
+ * Sets whether document has been changed or not. The editor is tracking changes
+ * automatically, but sometimes it's necessary to change the dirty flag to force
+ * "Save changes" dialog for example.
+ */
+void
+e_html_editor_view_set_changed (EHTMLEditorView *view,
+ gboolean changed)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ if (view->priv->changed == changed)
+ return;
+
+ view->priv->changed = changed;
+
+ g_object_notify (G_OBJECT (view), "changed");
+}
+
+static gboolean
+is_citation_node (WebKitDOMNode *node)
+{
+ char *value;
+
+ if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
+ return FALSE;
+
+ value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+ /* citation == <blockquote type='cite'> */
+ if (g_strcmp0 (value, "cite") == 0) {
+ g_free (value);
+ return TRUE;
+ } else {
+ g_free (value);
+ return FALSE;
+ }
+}
+
+static gchar *
+get_quotation_for_level (gint quote_level)
+{
+ gint ii;
+ GString *output = g_string_new ("");
+
+ for (ii = 0; ii < quote_level; ii++) {
+ g_string_append (output, "<span class=\"quote_character\">");
+ g_string_append (output, QUOTE_SYMBOL);
+ g_string_append (output, " ");
+ g_string_append (output, "</span>");
+ }
+
+ return g_string_free (output, FALSE);
+}
+
+static void
+insert_quote_symbols (WebKitDOMHTMLElement *element,
+ gint quote_level,
+ gboolean skip_first,
+ gboolean insert_newline)
+{
+ gchar *text;
+ gint ii;
+ GString *output;
+ gchar *quotation;
+
+ if (!WEBKIT_DOM_IS_HTML_ELEMENT (element))
+ return;
+
+ text = webkit_dom_html_element_get_inner_html (element);
+ output = g_string_new ("");
+ quotation = get_quotation_for_level (quote_level);
+
+ if (g_strcmp0 (text, "\n") == 0) {
+ g_string_append (output, "<span class=\"-x-evo-quoted\">");
+ g_string_append (output, quotation);
+ g_string_append (output, "</span>");
+ g_string_append (output, "\n");
+ } else {
+ gchar **lines;
+
+ lines = g_strsplit (text, "\n", 0);
+
+ for (ii = 0; lines[ii]; ii++) {
+ if (ii == 0 && skip_first) {
+ if (g_strv_length (lines) == 1) {
+ g_strfreev (lines);
+ goto exit;
+ }
+ g_string_append (output, lines[ii]);
+ g_string_append (output, "\n");
+ }
+
+ g_string_append (output, "<span class=\"-x-evo-quoted\">");
+ g_string_append (output, quotation);
+ g_string_append (output, "</span>");
+
+ /* Insert line of text */
+ g_string_append (output, lines[ii]);
+ if ((ii == g_strv_length (lines) - 1) &&
+ !g_str_has_suffix (text, "\n") && !insert_newline) {
+ /* If we are on last line and node's text doesn't
+ * end with \n, don't insert it */
+ break;
+ }
+ g_string_append (output, "\n");
+ }
+
+ g_strfreev (lines);
+ }
+
+ webkit_dom_html_element_set_inner_html (element, output->str, NULL);
+ exit:
+ g_free (quotation);
+ g_free (text);
+ g_string_free (output, TRUE);
+}
+
+static void
+quote_node (WebKitDOMDocument *document,
+ WebKitDOMNode *node,
+ gint quote_level)
+{
+ gboolean skip_first = FALSE;
+ gboolean insert_newline = FALSE;
+ gboolean is_html_node = FALSE;
+ WebKitDOMElement *wrapper;
+ WebKitDOMNode *node_clone, *prev_sibling, *next_sibling;
+
+ /* Don't quote when we are not in citation */
+ if (quote_level == 0)
+ return;
+
+ if (WEBKIT_DOM_IS_COMMENT (node))
+ return;
+
+ if (WEBKIT_DOM_IS_HTML_ELEMENT (node)) {
+ insert_quote_symbols (
+ WEBKIT_DOM_HTML_ELEMENT (node), quote_level, FALSE, FALSE);
+ return;
+ }
+
+ prev_sibling = webkit_dom_node_get_previous_sibling (node);
+ next_sibling = webkit_dom_node_get_next_sibling (node);
+
+ is_html_node =
+ !WEBKIT_DOM_IS_COMMENT (prev_sibling) && (
+ WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling) ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "b") ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "i") ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "u"));
+
+ if (prev_sibling && is_html_node)
+ skip_first = TRUE;
+
+ /* Skip the BR between first blockquote and pre */
+ if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling))
+ return;
+
+ if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) &&
+ WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (webkit_dom_node_get_next_sibling (next_sibling))) {
+ insert_newline = TRUE;
+ }
+
+ /* Do temporary wrapper */
+ wrapper = webkit_dom_document_create_element (document, "SPAN", NULL);
+ webkit_dom_element_set_class_name (wrapper, "-x-evo-temp-text-wrapper");
+
+ node_clone = webkit_dom_node_clone_node (node, TRUE);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (wrapper),
+ node_clone,
+ NULL);
+
+ insert_quote_symbols (
+ WEBKIT_DOM_HTML_ELEMENT (wrapper),
+ quote_level,
+ skip_first,
+ insert_newline);
+
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (wrapper),
+ node,
+ NULL);
+}
+
+static void
+insert_quote_symbols_before_node (WebKitDOMDocument *document,
+ WebKitDOMNode *node,
+ gint quote_level,
+ gboolean is_html_node)
+{
+ gchar *quotation;
+ WebKitDOMElement *element;
+
+ quotation = get_quotation_for_level (quote_level);
+ element = webkit_dom_document_create_element (document, "SPAN", NULL);
+ element_add_class (element, "-x-evo-quoted");
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (element), quotation, NULL);
+
+ if (is_html_node) {
+ WebKitDOMElement *new_br;
+
+ new_br = webkit_dom_document_create_element (document, "br", NULL);
+ element_add_class (new_br, "-x-evo-temp-br");
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (new_br),
+ node,
+ NULL);
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (node),
+ WEBKIT_DOM_NODE (element),
+ node,
+ NULL);
+
+ if (is_html_node) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node,
+ NULL);
+ }
+
+ g_free (quotation);
+}
+
+static void
+quote_plain_text_recursive (WebKitDOMDocument *document,
+ WebKitDOMNode *node,
+ WebKitDOMNode *start_node,
+ gint quote_level)
+{
+ gboolean skip_node = FALSE;
+ gboolean move_next = FALSE;
+ gboolean suppress_next = FALSE;
+ gboolean is_html_node = FALSE;
+ WebKitDOMNode *next_sibling, *prev_sibling;
+
+ node = webkit_dom_node_get_first_child (node);
+
+ while (node) {
+ skip_node = FALSE;
+ move_next = FALSE;
+ is_html_node = FALSE;
+
+ if (WEBKIT_DOM_IS_COMMENT (node))
+ goto next_node;
+
+ prev_sibling = webkit_dom_node_get_previous_sibling (node);
+ next_sibling = webkit_dom_node_get_next_sibling (node);
+
+ if (WEBKIT_DOM_IS_TEXT (node)) {
+ /* Start quoting after we are in blockquote */
+ if (quote_level > 0 && !suppress_next) {
+ /* When quoting text node, we are wrappering it and
+ * afterwards replacing it with that wrapper, thus asking
+ * for next_sibling after quoting will return NULL bacause
+ * that node don't exist anymore */
+ quote_node (document, node, quote_level);
+ node = next_sibling;
+ skip_node = TRUE;
+ } else
+ suppress_next = FALSE;
+
+ goto next_node;
+ }
+
+ if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node)))
+ goto next_node;
+
+ if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position")) {
+ if (quote_level > 0)
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-quoting");
+
+ move_next = TRUE;
+ suppress_next = TRUE;
+ goto next_node;
+ }
+
+ if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-selection-start-marker") ||
+ element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-selection-end-marker")) {
+ move_next = TRUE;
+ suppress_next = TRUE;
+ goto next_node;
+ }
+
+ if (WEBKIT_DOM_IS_HTML_META_ELEMENT (node)) {
+ goto next_node;
+ }
+ if (WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node)) {
+ move_next = TRUE;
+ goto next_node;
+ }
+ if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) {
+ move_next = TRUE;
+ goto next_node;
+ }
+
+ if (webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0)
+ goto with_children;
+
+ /* Even in plain text mode we can have some basic html element
+ * like anchor and others. When Forwaring e-mail as Quoted EMFormat
+ * generates header that contatains <b> tags (bold font).
+ * We have to treat these elements separately to avoid
+ * modifications of theirs inner texts */
+ is_html_node =
+ WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") ||
+ element_has_tag (WEBKIT_DOM_ELEMENT (node), "u");
+
+ if (is_html_node) {
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling))
+ insert_quote_symbols_before_node (
+ document, prev_sibling, quote_level, TRUE);
+
+ move_next = TRUE;
+ goto next_node;
+ }
+
+ /* If element doesn't have children, we can quote it */
+ if (is_citation_node (node)) {
+ /* Citation with just text inside */
+ quote_node (document, node, quote_level + 1);
+ /* Set citation as quoted */
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (node),
+ "-x-evo-plaintext-quoted");
+
+ move_next = TRUE;
+ goto next_node;
+ }
+
+ if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (node))
+ goto not_br;
+
+ if (!prev_sibling) {
+ WebKitDOMNode *parent;
+
+ parent = webkit_dom_node_get_parent_node (node);
+
+ /* BR in the beginning of the citation */
+ if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent))
+ insert_quote_symbols_before_node (
+ document, node, quote_level, FALSE);
+ }
+
+ if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+ WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (next_sibling) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) {
+ /* Situation when anchors are alone on line */
+ gchar *text_content;
+
+ text_content = webkit_dom_node_get_text_content (prev_sibling);
+
+ if (g_str_has_suffix (text_content, "\n")) {
+ insert_quote_symbols_before_node (
+ document, node, quote_level, FALSE);
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node,
+ NULL);
+ g_free (text_content);
+ node = next_sibling;
+ skip_node = TRUE;
+ goto next_node;
+ }
+ g_free (text_content);
+ }
+
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) {
+ gchar *quotation, *content;
+
+ quotation = get_quotation_for_level (quote_level);
+
+ content = g_strconcat (
+ "<span class=\"-x-evo-quoted\">",
+ quotation,
+ "</span><br class=\"-x-evo-temp-br\">",
+ NULL);
+
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (node),
+ content,
+ NULL);
+
+ g_free (content);
+ g_free (quotation);
+
+ node = next_sibling;
+ skip_node = TRUE;
+ goto next_node;
+ }
+
+ if (!prev_sibling && !next_sibling) {
+ WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+ if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) {
+ insert_quote_symbols_before_node (
+ document, node, quote_level, FALSE);
+ }
+ }
+
+ if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) {
+ gchar *text_content;
+
+ text_content = webkit_dom_node_get_text_content (prev_sibling);
+ if (g_strcmp0 (text_content, "") == 0)
+ insert_quote_symbols_before_node (
+ document, node, quote_level, FALSE);
+
+ g_free (text_content);
+ }
+
+ if (is_citation_node (prev_sibling)) {
+ insert_quote_symbols_before_node (
+ document, node, quote_level, FALSE);
+ }
+ not_br:
+ if (g_strcmp0 (webkit_dom_node_get_text_content (node), "") == 0) {
+ move_next = TRUE;
+ goto next_node;
+ }
+
+ quote_node (document, node, quote_level);
+
+ move_next = TRUE;
+ goto next_node;
+
+ with_children:
+ if (is_citation_node (node)) {
+ /* Go deeper and increase level */
+ quote_plain_text_recursive (
+ document, node, start_node, quote_level + 1);
+ /* set citation as quoted */
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (node),
+ "-x-evo-plaintext-quoted");
+ move_next = TRUE;
+ } else {
+ quote_plain_text_recursive (
+ document, node, start_node, quote_level);
+ move_next = TRUE;
+ }
+ next_node:
+ if (!skip_node) {
+ /* Move to next node */
+ if (!move_next && webkit_dom_node_has_child_nodes (node)) {
+ node = webkit_dom_node_get_first_child (node);
+ } else if (webkit_dom_node_get_next_sibling (node)) {
+ node = webkit_dom_node_get_next_sibling (node);
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+static gint
+get_citation_level (WebKitDOMNode *node,
+ gboolean set_plaintext_quoted)
+{
+ WebKitDOMNode *parent = node;
+ gint level = 0;
+
+ while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+ webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) {
+ level++;
+
+ if (set_plaintext_quoted) {
+ element_add_class (
+ WEBKIT_DOM_ELEMENT (parent),
+ "-x-evo-plaintext-quoted");
+ }
+ }
+
+ parent = webkit_dom_node_get_parent_node (parent);
+ }
+
+ return level;
+}
+
+WebKitDOMElement *
+e_html_editor_view_quote_plain_text_element (EHTMLEditorView *view,
+ WebKitDOMElement *element)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNode *element_clone;
+ WebKitDOMNodeList *list;
+ gint ii, length, level;
+
+ document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+
+ element_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE);
+ level = get_citation_level (WEBKIT_DOM_NODE (element), TRUE);
+
+ /* Remove old quote characters if the exists */
+ list = webkit_dom_element_query_selector_all (
+ WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node), node, NULL);
+ }
+
+ quote_plain_text_recursive (
+ document, element_clone, element_clone, level);
+
+ /* Replace old element with one, that is quoted */
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+ element_clone,
+ WEBKIT_DOM_NODE (element),
+ NULL);
+
+ return WEBKIT_DOM_ELEMENT (element_clone);
+}
+
+/**
+ * e_html_editor_view_quote_plain_text:
+ * @view: an #EHTMLEditorView
+ *
+ * Quote text inside citation blockquotes in plain text mode.
+ *
+ * As this function is cloning and replacing all citation blockquotes keep on
+ * mind that any pointers to nodes inside these blockquotes will be invalidated.
+ */
+WebKitDOMElement *
+e_html_editor_view_quote_plain_text (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMNode *body_clone;
+ WebKitDOMNamedNodeMap *attributes;
+ WebKitDOMNodeList *list;
+ WebKitDOMElement *element;
+ gint ii, length;
+ gulong attributes_length;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ /* Check if the document is already quoted */
+ element = webkit_dom_document_query_selector (
+ document, ".-x-evo-plaintext-quoted", NULL);
+ if (element)
+ return NULL;
+
+ body = webkit_dom_document_get_body (document);
+ body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE);
+
+ /* Clean unwanted spaces before and after blockquotes */
+ list = webkit_dom_element_query_selector_all (
+ WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii);
+ WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote);
+ WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote);
+
+ if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (prev_sibling),
+ prev_sibling,
+ NULL);
+ }
+ if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (next_sibling),
+ next_sibling,
+ NULL);
+ }
+ if (webkit_dom_node_has_child_nodes (blockquote)) {
+ WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote);
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) {
+ webkit_dom_node_remove_child (
+ blockquote,
+ child,
+ NULL);
+ }
+ }
+ }
+
+ quote_plain_text_recursive (document, body_clone, body_clone, 0);
+
+ /* Copy attributes */
+ attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+ attributes_length = webkit_dom_named_node_map_get_length (attributes);
+ for (ii = 0; ii < attributes_length; ii++) {
+ gchar *name, *value;
+ WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+ name = webkit_dom_node_get_local_name (node);
+ value = webkit_dom_node_get_node_value (node);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL);
+
+ g_free (name);
+ g_free (value);
+ }
+
+ /* Replace old BODY with one, that is quoted */
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)),
+ body_clone,
+ WEBKIT_DOM_NODE (body),
+ NULL);
+
+ return WEBKIT_DOM_ELEMENT (body_clone);
+}
+
+/**
+ * e_html_editor_view_dequote_plain_text:
+ * @view: an #EHTMLEditorView
+ *
+ * Dequote already quoted plain text in editor.
+ * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise
+ * it's not working.
+ */
+void
+e_html_editor_view_dequote_plain_text (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *paragraphs;
+ gint length, ii;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ paragraphs = webkit_dom_document_query_selector_all (
+ document, "blockquote.-x-evo-plaintext-quoted", NULL);
+ length = webkit_dom_node_list_get_length (paragraphs);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNodeList *list;
+ WebKitDOMElement *element;
+ gint jj, list_length;
+
+ element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii));
+
+ if (is_citation_node (WEBKIT_DOM_NODE (element))) {
+ element_remove_class (element, "-x-evo-plaintext-quoted");
+
+ list = webkit_dom_element_query_selector_all (
+ element, "span.-x-evo-quoted", NULL);
+ list_length = webkit_dom_node_list_get_length (list);
+ for (jj = 0; jj < list_length; jj++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node,
+ NULL);
+ }
+ list = webkit_dom_element_query_selector_all (
+ element, "span.-x-evo-temp-text-wrapper", NULL);
+ list_length = webkit_dom_node_list_get_length (list);
+ for (jj = 0; jj < list_length; jj++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (node),
+ webkit_dom_node_get_first_child (node),
+ node,
+ NULL);
+ }
+ }
+ }
+}
+
+/**
+ * e_html_editor_view_get_html_mode:
+ * @view: an #EHTMLEditorView
+ *
+ * Whether the editor is in HTML mode or plain text mode. In HTML mode,
+ * more formatting options are avilable an the email is sent as
+ * multipart/alternative.
+ *
+ * Returns: @TRUE when HTML mode is enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_html_mode (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+ return view->priv->html_mode;
+}
+
+static gint
+get_indentation_level (WebKitDOMElement *element)
+{
+ WebKitDOMElement *parent;
+ gint level = 1;
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+ /* Count level of indentation */
+ while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+ if (element_has_class (parent, "-x-evo-indented"))
+ level++;
+
+ parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
+ }
+
+ return level;
+}
+
+static void
+process_blockquote (WebKitDOMElement *blockquote)
+{
+ WebKitDOMNodeList *list;
+ int jj, length;
+
+ /* First replace wrappers */
+ list = webkit_dom_element_query_selector_all (
+ blockquote, "span.-x-evo-temp-text-wrapper", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (jj = 0; jj < length; jj++) {
+ WebKitDOMNode *quoted_node;
+ gchar *text_content, *tmp = NULL;
+
+ quoted_node = webkit_dom_node_list_item (list, jj);
+ text_content = webkit_dom_node_get_text_content (quoted_node);
+ if (webkit_dom_node_get_previous_sibling (quoted_node)) {
+ tmp = g_strconcat ("<br>", text_content, NULL);
+ g_free (text_content);
+ text_content = tmp;
+ }
+
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL);
+
+ g_free (text_content);
+ }
+
+ /* Afterwards replace quote nodes with symbols */
+ list = webkit_dom_element_query_selector_all (
+ blockquote, "span.-x-evo-quoted", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (jj = 0; jj < length; jj++) {
+ WebKitDOMNode *quoted_node;
+ gchar *text_content, *tmp = NULL;
+
+ quoted_node = webkit_dom_node_list_item (list, jj);
+ text_content = webkit_dom_node_get_text_content (quoted_node);
+ if (webkit_dom_node_get_previous_sibling (quoted_node)) {
+ tmp = g_strconcat ("<br>", text_content, NULL);
+ g_free (text_content);
+ text_content = tmp;
+ }
+
+ webkit_dom_html_element_set_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL);
+
+ g_free (text_content);
+ }
+
+ if (element_has_class (blockquote, "-x-evo-indented")) {
+ WebKitDOMNode *child;
+ gchar *spaces;
+
+ spaces = g_strnfill (4 * get_indentation_level (blockquote), ' ');
+
+ child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote));
+ while (child) {
+ /* If next sibling is indented blockqoute skip it,
+ * it will be processed afterwards */
+ if (WEBKIT_DOM_IS_ELEMENT (child) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented"))
+ child = webkit_dom_node_get_next_sibling (child);
+
+ if (WEBKIT_DOM_IS_TEXT (child)) {
+ gchar *text_content;
+ gchar *indented_text;
+
+ text_content = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (child));
+ indented_text = g_strconcat (spaces, text_content, NULL);
+
+ webkit_dom_text_replace_whole_text (
+ WEBKIT_DOM_TEXT (child),
+ indented_text,
+ NULL);
+
+ g_free (text_content);
+ g_free (indented_text);
+ }
+
+ if (!child)
+ break;
+
+ /* Move to next node */
+ if (webkit_dom_node_has_child_nodes (child))
+ child = webkit_dom_node_get_first_child (child);
+ else if (webkit_dom_node_get_next_sibling (child))
+ child = webkit_dom_node_get_next_sibling (child);
+ else {
+ if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (blockquote), child))
+ break;
+
+ child = webkit_dom_node_get_parent_node (child);
+ if (child)
+ child = webkit_dom_node_get_next_sibling (child);
+ }
+ }
+ g_free (spaces);
+
+ webkit_dom_element_remove_attribute (blockquote, "style");
+ }
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_alpha_value (gint value,
+ gboolean lower)
+{
+ GString *str;
+ gchar *rv;
+ gint add = lower ? 'a' : 'A';
+
+ str = g_string_new (". ");
+
+ do {
+ g_string_prepend_c (str, ((value - 1) % 26) + add);
+ value = (value - 1) / 26;
+ } while (value);
+
+ rv = str->str;
+ g_string_free (str, FALSE);
+
+ return rv;
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_roman_value (gint value,
+ gboolean lower)
+{
+ GString *str;
+ const gchar *base = "IVXLCDM";
+ gchar *rv;
+ gint b, r, add = lower ? 'a' - 'A' : 0;
+
+ if (value > 3999)
+ return g_strdup ("?. ");
+
+ str = g_string_new (". ");
+
+ for (b = 0; value > 0 && b < 7 - 1; b += 2, value /= 10) {
+ r = value % 10;
+ if (r != 0) {
+ if (r < 4) {
+ for (; r; r--)
+ g_string_prepend_c (str, base[b] + add);
+ } else if (r == 4) {
+ g_string_prepend_c (str, base[b + 1] + add);
+ g_string_prepend_c (str, base[b] + add);
+ } else if (r == 5) {
+ g_string_prepend_c (str, base[b + 1] + add);
+ } else if (r < 9) {
+ for (; r > 5; r--)
+ g_string_prepend_c (str, base[b] + add);
+ g_string_prepend_c (str, base[b + 1] + add);
+ } else if (r == 9) {
+ g_string_prepend_c (str, base[b + 2] + add);
+ g_string_prepend_c (str, base[b] + add);
+ }
+ }
+ }
+
+ rv = str->str;
+ g_string_free (str, FALSE);
+
+ return rv;
+}
+
+static void
+process_list_to_plain_text (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ gint level,
+ GString *output)
+{
+ EHTMLEditorSelectionBlockFormat format;
+ EHTMLEditorSelectionAlignment alignment;
+ gint counter = 1;
+ gchar *indent_per_level = g_strnfill (SPACES_PER_LIST_LEVEL, ' ');
+ WebKitDOMNode *item;
+ gint word_wrap_length = e_html_editor_selection_get_word_wrap_length (
+ e_html_editor_view_get_selection (view));
+
+ format = e_html_editor_selection_get_list_format_from_node (
+ WEBKIT_DOM_NODE (element));
+
+ /* Process list items to plain text */
+ item = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+ while (item) {
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (item))
+ g_string_append (output, "\n");
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+ gchar *space, *item_str = NULL;
+ gint ii = 0;
+ WebKitDOMElement *wrapped;
+ GString *item_value = g_string_new ("");
+
+ alignment = e_html_editor_selection_get_list_alignment_from_node (
+ WEBKIT_DOM_NODE (item));
+
+ wrapped = webkit_dom_element_query_selector (
+ WEBKIT_DOM_ELEMENT (item), ".-x-evo-wrap-br", NULL);
+ /* Wrapped text */
+ if (wrapped) {
+ WebKitDOMNode *node = webkit_dom_node_get_first_child (item);
+ GString *line = g_string_new ("");
+ while (node) {
+ if (WEBKIT_DOM_IS_TEXT (node)) {
+ /* append text from line */
+ gchar *text_content;
+ text_content = webkit_dom_node_get_text_content (node);
+ g_string_append (line, text_content);
+ g_free (text_content);
+ }
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node) &&
+ element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+ /* put spaces before line characters -> wordwraplength - indentation */
+ g_string_append (line, "\n");
+ /* put spaces before line characters -> wordwraplength - indentation */
+ for (ii = 0; ii < level; ii++)
+ g_string_append (line, indent_per_level);
+ g_string_append (item_value, line->str);
+ g_string_erase (line, 0, -1);
+ }
+ node = webkit_dom_node_get_next_sibling (node);
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+ g_string_append (item_value, line->str);
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+ gchar *fill = NULL;
+ gint fill_length;
+
+ fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+ fill_length -= ii * SPACES_PER_LIST_LEVEL;
+ fill_length /= 2;
+
+ if (fill_length < 0)
+ fill_length = 0;
+
+ fill = g_strnfill (fill_length, ' ');
+
+ g_string_append (item_value, fill);
+ g_string_append (item_value, line->str);
+ g_free (fill);
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+ gchar *fill = NULL;
+ gint fill_length;
+
+ fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+ fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+ if (fill_length < 0)
+ fill_length = 0;
+
+ fill = g_strnfill (fill_length, ' ');
+
+ g_string_append (item_value, fill);
+ g_string_append (item_value, line->str);
+ g_free (fill);
+ }
+ g_string_free (line, TRUE);
+ /* that same here */
+ } else {
+ gchar *text_content =
+ webkit_dom_node_get_text_content (item);
+ g_string_append (item_value, text_content);
+ g_free (text_content);
+ }
+
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST) {
+ space = g_strnfill (SPACES_PER_LIST_LEVEL - 2, ' ');
+ item_str = g_strdup_printf (
+ "%s* %s", space, item_value->str);
+ g_free (space);
+ }
+
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) {
+ gint length = 1, tmp = counter;
+
+ while ((tmp = tmp / 10) > 1)
+ length++;
+
+ if (tmp == 1)
+ length++;
+
+ space = g_strnfill (SPACES_PER_LIST_LEVEL - 2 - length, ' ');
+ item_str = g_strdup_printf (
+ "%s%d. %s", space, counter, item_value->str);
+ g_free (space);
+ }
+
+ if (format > E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) {
+ gchar *value;
+
+ if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+ value = get_alpha_value (counter, FALSE);
+ else
+ value = get_roman_value (counter, FALSE);
+
+ /* Value already containes dot and space */
+ space = g_strnfill (SPACES_PER_LIST_LEVEL - strlen (value), ' ');
+ item_str = g_strdup_printf (
+ "%s%s%s", space, value, item_value->str);
+ g_free (space);
+ g_free (value);
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT) {
+ for (ii = 0; ii < level - 1; ii++) {
+ g_string_append (output, indent_per_level);
+ }
+ g_string_append (output, item_str);
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+ if (!wrapped) {
+ gchar *fill = NULL;
+ gint fill_length;
+
+ fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+ fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+ if (fill_length < 0)
+ fill_length = 0;
+
+ if (g_str_has_suffix (item_str, " "))
+ fill_length++;
+
+ fill = g_strnfill (fill_length, ' ');
+
+ g_string_append (output, fill);
+ g_free (fill);
+ }
+ if (g_str_has_suffix (item_str, " "))
+ g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1);
+ else
+ g_string_append (output, item_str);
+ }
+
+ if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+ if (!wrapped) {
+ gchar *fill = NULL;
+ gint fill_length = 0;
+
+ for (ii = 0; ii < level - 1; ii++)
+ g_string_append (output, indent_per_level);
+
+ fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+ fill_length -= ii * SPACES_PER_LIST_LEVEL;
+ fill_length /= 2;
+
+ if (fill_length < 0)
+ fill_length = 0;
+
+ if (g_str_has_suffix (item_str, " "))
+ fill_length++;
+
+ fill = g_strnfill (fill_length, ' ');
+
+ g_string_append (output, fill);
+ g_free (fill);
+ }
+ if (g_str_has_suffix (item_str, " "))
+ g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1);
+ else
+ g_string_append (output, item_str);
+ }
+
+ counter++;
+ item = webkit_dom_node_get_next_sibling (item);
+ if (item)
+ g_string_append (output, "\n");
+
+ g_free (item_str);
+ g_string_free (item_value, TRUE);
+ } else if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (item) ||
+ WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (item)) {
+ process_list_to_plain_text (
+ view, WEBKIT_DOM_ELEMENT (item), level + 1, output);
+ item = webkit_dom_node_get_next_sibling (item);
+ } else {
+ item = webkit_dom_node_get_next_sibling (item);
+ }
+ }
+
+ if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)))
+ g_string_append (output, "\n");
+
+ g_free (indent_per_level);
+}
+
+static void
+remove_base_attributes (WebKitDOMElement *element)
+{
+ webkit_dom_element_remove_attribute (element, "class");
+ webkit_dom_element_remove_attribute (element, "id");
+ webkit_dom_element_remove_attribute (element, "name");
+}
+
+static void
+remove_evolution_attributes (WebKitDOMElement *element)
+{
+ webkit_dom_element_remove_attribute (element, "x-evo-smiley");
+ webkit_dom_element_remove_attribute (element, "data-converted");
+ webkit_dom_element_remove_attribute (element, "data-edit-as-new");
+ webkit_dom_element_remove_attribute (element, "data-inline");
+ webkit_dom_element_remove_attribute (element, "data-uri");
+ webkit_dom_element_remove_attribute (element, "data-message");
+ webkit_dom_element_remove_attribute (element, "data-name");
+ webkit_dom_element_remove_attribute (element, "data-new-message");
+ webkit_dom_element_remove_attribute (element, "spellcheck");
+}
+/*
+static void
+remove_style_attributes (WebKitDOMElement *element)
+{
+ webkit_dom_element_remove_attribute (element, "bgcolor");
+ webkit_dom_element_remove_attribute (element, "background");
+ webkit_dom_element_remove_attribute (element, "style");
+}
+*/
+static void
+process_elements (EHTMLEditorView *view,
+ WebKitDOMNode *node,
+ gboolean to_html,
+ gboolean changing_mode,
+ gboolean to_plain_text,
+ GString *buffer)
+{
+ WebKitDOMNodeList *nodes;
+ gulong ii, length;
+ gchar *content;
+ gboolean skip_nl = FALSE;
+
+ if (to_plain_text && !buffer)
+ return;
+
+ if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
+ if (changing_mode && to_plain_text) {
+ WebKitDOMNamedNodeMap *attributes;
+ gulong attributes_length;
+
+ /* Copy attributes */
+ g_string_append (buffer, "<html><head></head><body ");
+ attributes = webkit_dom_element_get_attributes (
+ WEBKIT_DOM_ELEMENT (node));
+ attributes_length =
+ webkit_dom_named_node_map_get_length (attributes);
+
+ for (ii = 0; ii < attributes_length; ii++) {
+ gchar *name, *value;
+ WebKitDOMNode *node =
+ webkit_dom_named_node_map_item (
+ attributes, ii);
+
+ name = webkit_dom_node_get_local_name (node);
+ value = webkit_dom_node_get_node_value (node);
+
+ g_string_append (buffer, name);
+ g_string_append (buffer, "=\"");
+ g_string_append (buffer, value);
+ g_string_append (buffer, "\" ");
+
+ g_free (name);
+ g_free (value);
+ }
+ g_string_append (buffer, ">");
+ }
+ if (to_html)
+ remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node));
+ }
+
+ nodes = webkit_dom_node_get_child_nodes (node);
+ length = webkit_dom_node_list_get_length (nodes);
+
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *child;
+ gboolean skip_node = FALSE;
+
+ child = webkit_dom_node_list_item (nodes, ii);
+
+ if (WEBKIT_DOM_IS_TEXT (child)) {
+ gchar *content, *tmp;
+ GRegex *regex;
+
+ content = webkit_dom_node_get_text_content (child);
+ /* Replace tabs with 4 whitespaces, otherwise they got
+ * replaced by single whitespace */
+ if (strstr (content, "\x9")) {
+ regex = g_regex_new ("\x9", 0, 0, NULL);
+ tmp = g_regex_replace (
+ regex, content, -1, 0, " ", 0, NULL);
+ webkit_dom_node_set_text_content (child, tmp, NULL);
+ g_free (content);
+ g_free (tmp);
+ content = webkit_dom_node_get_text_content (child);
+ g_regex_unref (regex);
+ }
+
+ if (strstr (content, UNICODE_ZERO_WIDTH_SPACE)) {
+ regex = g_regex_new (UNICODE_ZERO_WIDTH_SPACE, 0, 0, NULL);
+ tmp = g_regex_replace (
+ regex, content, -1, 0, "", 0, NULL);
+ webkit_dom_node_set_text_content (child, tmp, NULL);
+ g_free (tmp);
+ g_free (content);
+ content = webkit_dom_node_get_text_content (child);
+ g_regex_unref (regex);
+ }
+
+ if (to_plain_text && !changing_mode) {
+ gchar *style;
+ const gchar *css_align;
+
+ style = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "style");
+
+ if ((css_align = strstr (style, "text-align: "))) {
+ gchar *align;
+ gchar *content_with_align;
+ gint length;
+ gint word_wrap_length =
+ e_html_editor_selection_get_word_wrap_length (
+ e_html_editor_view_get_selection (view));
+
+ if (!g_str_has_prefix (css_align + 12, "left")) {
+ if (g_str_has_prefix (css_align + 12, "center"))
+ length = (word_wrap_length - g_utf8_strlen (content, -1)) / 2;
+ else
+ length = word_wrap_length - g_utf8_strlen (content, -1);
+
+ if (g_str_has_suffix (content, " ")) {
+ char *tmp;
+
+ length++;
+ align = g_strnfill (length, ' ');
+
+ tmp = g_strndup (content, g_utf8_strlen (content, -1) -1);
+
+ content_with_align = g_strconcat (
+ align, tmp, NULL);
+ g_free (tmp);
+ } else {
+ align = g_strnfill (length, ' ');
+
+ content_with_align = g_strconcat (
+ align, content, NULL);
+ }
+
+ g_free (content);
+ g_free (align);
+ content = content_with_align;
+ }
+ }
+
+ g_free (style);
+ }
+
+ if (to_plain_text || changing_mode)
+ g_string_append (buffer, content);
+
+ g_free (content);
+
+ goto next;
+ }
+
+ if (WEBKIT_DOM_IS_COMMENT (child) || !WEBKIT_DOM_IS_ELEMENT (child))
+ goto next;
+
+ /* Leave caret position untouched */
+ if (element_has_id (WEBKIT_DOM_ELEMENT (child), "-x-evo-caret-position")) {
+ if (changing_mode && to_plain_text) {
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ g_string_append (buffer, content);
+ g_free (content);
+ }
+ if (to_html) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (child),
+ child,
+ NULL);
+ }
+
+ skip_node = TRUE;
+ }
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (child), "Apple-tab-span")) {
+ if (!changing_mode && to_plain_text) {
+ gchar *content, *tmp;
+ GRegex *regex;
+
+ content = webkit_dom_node_get_text_content (child);
+ /* Replace tabs with 4 whitespaces, otherwise they got
+ * replaced by single whitespace */
+ if (strstr (content, "\x9")) {
+ regex = g_regex_new ("\x9", 0, 0, NULL);
+ tmp = g_regex_replace (
+ regex, content, -1, 0, " ", 0, NULL);
+ g_string_append (buffer, tmp);
+ g_free (tmp);
+ content = webkit_dom_node_get_text_content (child);
+ g_regex_unref (regex);
+ }
+ }
+ if (to_html) {
+ element_remove_class (
+ WEBKIT_DOM_ELEMENT (child),
+ "Applet-tab-span");
+ }
+
+ skip_node = TRUE;
+ }
+
+ /* Leave blockquotes as they are */
+ if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) {
+ if (changing_mode && to_plain_text) {
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ g_string_append (buffer, content);
+ g_free (content);
+ skip_node = TRUE;
+ } else {
+ if (!changing_mode && to_plain_text) {
+ if (get_citation_level (child, FALSE) == 0) {
+ gchar *value;
+ value = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (child), "type");
+ if (value && g_strcmp0 (value, "cite") == 0) {
+ g_string_append (buffer, "\n");
+ }
+ g_free (value);
+ }
+ }
+ process_blockquote (WEBKIT_DOM_ELEMENT (child));
+ }
+ }
+
+ if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (child) ||
+ WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (child)) {
+ if (to_plain_text) {
+ if (changing_mode) {
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ g_string_append (buffer, content);
+ g_free (content);
+ } else {
+ process_list_to_plain_text (
+ view, WEBKIT_DOM_ELEMENT (child), 1, buffer);
+ }
+ skip_node = TRUE;
+ goto next;
+ }
+ }
+
+ if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-resizable-wrapper") &&
+ !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+ WebKitDOMNode *image =
+ webkit_dom_node_get_first_child (child);
+
+ if (to_html && WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image)) {
+ remove_evolution_attributes (
+ WEBKIT_DOM_ELEMENT (image));
+
+ webkit_dom_node_replace_child (
+ node, image, child, NULL);
+ }
+
+ skip_node = TRUE;
+ }
+
+ /* Leave paragraphs as they are */
+ if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-paragraph")) {
+ if (changing_mode && to_plain_text) {
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ g_string_append (buffer, content);
+ g_free (content);
+ skip_node = TRUE;
+ }
+ if (to_html) {
+ remove_base_attributes (WEBKIT_DOM_ELEMENT (child));
+ remove_evolution_attributes (WEBKIT_DOM_ELEMENT (child));
+ }
+ }
+
+ /* Signature */
+ if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child)) {
+ WebKitDOMNode *first_child;
+
+ first_child = webkit_dom_node_get_first_child (child);
+ if (WEBKIT_DOM_IS_ELEMENT (first_child)) {
+ if (element_has_class (
+ WEBKIT_DOM_ELEMENT (first_child),
+ "-x-evo-signature")) {
+
+ if (to_html) {
+ remove_base_attributes (
+ WEBKIT_DOM_ELEMENT (first_child));
+ remove_evolution_attributes (
+ WEBKIT_DOM_ELEMENT (first_child));
+ }
+ if (to_plain_text && !changing_mode) {
+ g_string_append (buffer, "\n");
+ content = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (first_child));
+ g_string_append (buffer, content);
+ g_free (content);
+ skip_nl = TRUE;
+ }
+ skip_node = TRUE;
+ }
+ }
+ }
+
+ /* Replace smileys with their text representation */
+ if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+ if (to_plain_text && !changing_mode) {
+ WebKitDOMNode *text_version;
+
+ text_version = webkit_dom_node_get_last_child (child);
+ content = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (text_version));
+ g_string_append (buffer, content);
+ g_free (content);
+ skip_node = TRUE;
+ }
+ if (to_html) {
+ WebKitDOMElement *img;
+
+ img = WEBKIT_DOM_ELEMENT (
+ webkit_dom_node_get_first_child (child)),
+
+ remove_evolution_attributes (img);
+ remove_base_attributes (img);
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (child),
+ WEBKIT_DOM_NODE (img),
+ child,
+ NULL);
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (child),
+ child,
+ NULL);
+ skip_node = TRUE;
+ }
+ }
+
+ /* Leave PRE elements untouched */
+ if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (child)) {
+ if (changing_mode && to_plain_text) {
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ g_string_append (buffer, content);
+ g_free (content);
+ skip_node = TRUE;
+ }
+ if (to_html)
+ remove_evolution_attributes (WEBKIT_DOM_ELEMENT (child));
+ }
+
+ if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) {
+ if (to_plain_text) {
+ /* Insert new line when we hit BR element */
+ g_string_append (buffer, changing_mode ? "<br>" : "\n");
+ }
+ }
+ next:
+ if (webkit_dom_node_has_child_nodes (child) && !skip_node)
+ process_elements (
+ view, child, to_html, changing_mode, to_plain_text, buffer);
+ }
+
+ if (to_plain_text && (
+ WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node) ||
+ WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (node) ||
+ WEBKIT_DOM_IS_HTML_PRE_ELEMENT (node) ||
+ WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))) {
+
+ gboolean add_br = TRUE;
+ WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node);
+
+ /* If we don't have next sibling (last element in body) or next element is
+ * signature we are not adding the BR element */
+ if (!next_sibling)
+ add_br = FALSE;
+
+ if (element_has_class (webkit_dom_node_get_parent_element (node), "-x-evo-indented"))
+ add_br = FALSE;
+
+ if (next_sibling && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (next_sibling)) {
+ if (webkit_dom_element_query_selector (
+ WEBKIT_DOM_ELEMENT (next_sibling),
+ "span.-x-evo-signature", NULL)) {
+
+ add_br = FALSE;
+ }
+ }
+
+ content = webkit_dom_node_get_text_content (node);
+ if (add_br && g_utf8_strlen (content, -1) > 0 && !skip_nl)
+ g_string_append (buffer, changing_mode ? "<br>" : "\n");
+
+ g_free (content);
+ }
+
+}
+
+static void
+remove_wrapping (EHTMLEditorView *view)
+{
+ gint length;
+ gint ii;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ list = webkit_dom_document_query_selector_all (document, "br.-x-evo-wrap-br", NULL);
+
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node), node, NULL);
+ }
+}
+
+static void
+remove_images_in_element (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ gboolean html_mode)
+{
+ gint length, ii;
+ WebKitDOMNodeList *images;
+
+ images = webkit_dom_element_query_selector_all (
+ element, "img:not(.-x-evo-smiley-img)", NULL);
+
+ length = webkit_dom_node_list_get_length (images);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *img = webkit_dom_node_list_item (images, ii);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (img), img, NULL);
+ }
+}
+
+static void
+remove_images (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ remove_images_in_element (
+ view,
+ WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)),
+ view->priv->html_mode);
+}
+
+static void
+toggle_smileys (EHTMLEditorView *view)
+{
+ gboolean html_mode;
+ gint length;
+ gint ii;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *smileys;
+
+ html_mode = e_html_editor_view_get_html_mode (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ smileys = webkit_dom_document_query_selector_all (
+ document, "img.-x-evo-smiley-img", NULL);
+
+ length = webkit_dom_node_list_get_length (smileys);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *img = webkit_dom_node_list_item (smileys, ii);
+ WebKitDOMNode *text = webkit_dom_node_get_next_sibling (img);
+ WebKitDOMElement *parent = webkit_dom_node_get_parent_element (img);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (html_mode ? text : img),
+ "style",
+ "display: none",
+ NULL);
+
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (html_mode ? img : text), "style");
+
+ if (html_mode)
+ element_add_class (parent, "-x-evo-resizable-wrapper");
+ else
+ element_remove_class (parent, "-x-evo-resizable-wrapper");
+ }
+}
+
+static void
+toggle_paragraphs_style_in_element (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ gboolean html_mode)
+{
+ EHTMLEditorSelection *selection;
+ gint ii, length;
+ WebKitDOMNodeList *paragraphs;
+
+ html_mode = e_html_editor_view_get_html_mode (view);
+ selection = e_html_editor_view_get_selection (view);
+
+ paragraphs = webkit_dom_element_query_selector_all (
+ element, ".-x-evo-paragraph", NULL);
+
+ length = webkit_dom_node_list_get_length (paragraphs);
+
+ for (ii = 0; ii < length; ii++) {
+ gchar *style;
+ const gchar *css_align;
+ WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii);
+
+ if (html_mode) {
+ style = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "style");
+
+ if ((css_align = strstr (style, "text-align: "))) {
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (node),
+ "style",
+ g_str_has_prefix (css_align + 12, "center") ?
+ "text-align: center" :
+ "text-align: right",
+ NULL);
+ } else {
+ /* In HTML mode the paragraphs don't have width limit */
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (node), "style");
+ }
+ g_free (style);
+ } else {
+ WebKitDOMNode *parent;
+
+ parent = webkit_dom_node_get_parent_node (node);
+ /* If the paragraph is inside indented paragraph don't set
+ * the style as it will be inherited */
+ if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) {
+ style = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "style");
+
+ if ((css_align = strstr (style, "text-align: "))) {
+ const gchar *style_to_add;
+
+ style_to_add = g_str_has_prefix (
+ css_align + 12, "center") ?
+ "text-align: center;" :
+ "text-align: right;";
+
+ /* In HTML mode the paragraphs have width limit */
+ e_html_editor_selection_set_paragraph_style (
+ selection, WEBKIT_DOM_ELEMENT (node),
+ -1, 0, style_to_add);
+ }
+ g_free (style);
+ }
+ }
+ }
+}
+
+static void
+toggle_paragraphs_style (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ toggle_paragraphs_style_in_element (
+ view,
+ WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)),
+ view->priv->html_mode);
+}
+
+static gchar *
+process_content_for_saving_as_draft (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMElement *element, *document_element;
+ gchar *content;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+ element = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-caret-position");
+
+ if (element)
+ webkit_dom_element_set_attribute (
+ element, "style", "display: none; color: red;", NULL);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-evo-draft", "", NULL);
+
+ document_element = webkit_dom_document_get_document_element (document);
+ content = webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (document_element));
+
+ webkit_dom_element_remove_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-evo-draft");
+
+ if (element)
+ webkit_dom_element_set_attribute (
+ element, "style", "color: red;", NULL);
+
+ return content;
+}
+
+static gchar *
+process_content_for_mode_change (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNode *body;
+ GString *plain_text;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+
+ plain_text = g_string_sized_new (1024);
+
+ process_elements (view, body, FALSE, TRUE, TRUE, plain_text);
+
+ g_string_append (plain_text, "</body></html>");
+
+ return g_string_free (plain_text, FALSE);
+}
+
+static void
+convert_element_from_html_to_plain_text (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ gboolean *wrap,
+ gboolean *quote)
+{
+ EHTMLEditorSelection *selection;
+ gint blockquotes_count;
+ gchar *inner_text, *inner_html;
+ gboolean restore = TRUE;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *top_signature, *signature, *blockquote, *main_blockquote;
+ WebKitDOMNode *signature_clone, *from;
+
+ selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+
+ top_signature = webkit_dom_element_query_selector (
+ element, ".-x-evo-top-signature", NULL);
+ signature = webkit_dom_element_query_selector (
+ element, "span.-x-evo-signature", NULL);
+ main_blockquote = webkit_dom_element_query_selector (
+ element, "#-x-evo-main-cite", NULL);
+
+ blockquote = webkit_dom_document_create_element (
+ document, "blockquote", NULL);
+
+ if (main_blockquote) {
+ WebKitDOMElement *input_start;
+
+ webkit_dom_element_set_attribute (
+ blockquote, "type", "cite", NULL);
+
+ input_start = webkit_dom_element_query_selector (
+ element, "#-x-evo-input-start", NULL);
+
+ restore = input_start ? TRUE : FALSE;
+
+ if (input_start) {
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (input_start),
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ NULL);
+ }
+ from = WEBKIT_DOM_NODE (main_blockquote);
+ } else {
+ if (signature) {
+ signature_clone = webkit_dom_node_clone_node (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature)),
+ TRUE);
+
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (element),
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (signature)),
+ NULL);
+ }
+ from = WEBKIT_DOM_NODE (element);
+ }
+
+ blockquotes_count = create_text_markers_for_citations_in_element (
+ WEBKIT_DOM_ELEMENT (from));
+
+ inner_text = webkit_dom_html_element_get_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (from));
+
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (blockquote), inner_text, NULL);
+
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (blockquote));
+
+ parse_html_into_paragraphs (
+ view, document,
+ main_blockquote ? blockquote : WEBKIT_DOM_ELEMENT (element),
+ inner_html,
+ FALSE);
+
+ if (main_blockquote) {
+ webkit_dom_node_replace_child (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (main_blockquote)),
+ WEBKIT_DOM_NODE (blockquote),
+ WEBKIT_DOM_NODE (main_blockquote),
+ NULL);
+
+ remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element));
+ *wrap = TRUE;
+ } else {
+ WebKitDOMNode *first_child;
+
+ if (signature) {
+ if (!top_signature) {
+ signature_clone = webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (element),
+ signature_clone,
+ NULL);
+ } else {
+ webkit_dom_node_insert_before (
+ WEBKIT_DOM_NODE (element),
+ signature_clone,
+ webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (element)),
+ NULL);
+ }
+ }
+
+ first_child = webkit_dom_node_get_first_child (
+ WEBKIT_DOM_NODE (element));
+
+ if (!webkit_dom_node_has_child_nodes (first_child)) {
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (first_child),
+ UNICODE_ZERO_WIDTH_SPACE,
+ NULL);
+ }
+ webkit_dom_node_insert_before (
+ first_child,
+ e_html_editor_selection_get_caret_position_node (
+ document),
+ webkit_dom_node_get_first_child (first_child),
+ NULL);
+
+ *wrap = TRUE;
+ }
+
+ *quote = main_blockquote || blockquotes_count > 0;
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-converted", "", NULL);
+
+ g_free (inner_text);
+ g_free (inner_html);
+
+ if (restore)
+ e_html_editor_selection_restore_caret_position (selection);
+}
+
+static gchar *
+process_content_for_plain_text (EHTMLEditorView *view)
+{
+ gboolean converted, wrap = FALSE, quote = FALSE, clean = FALSE;
+ gint length, ii;
+ GString *plain_text;
+ WebKitDOMDocument *document;
+ WebKitDOMNode *body, *source;
+ WebKitDOMNodeList *paragraphs;
+
+ plain_text = g_string_sized_new (1024);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+ converted = webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-converted");
+ source = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE);
+
+ /* If composer is in HTML mode we have to move the content to plain version */
+ if (view->priv->html_mode) {
+ if (converted) {
+ toggle_paragraphs_style_in_element (
+ view, WEBKIT_DOM_ELEMENT (source), FALSE);
+ remove_images_in_element (
+ view, WEBKIT_DOM_ELEMENT (source), FALSE);
+ } else {
+ gchar *inner_html;
+ WebKitDOMElement *div;
+
+ inner_html = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (body));
+
+ div = webkit_dom_document_create_element (
+ document, "div", NULL);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (div), inner_html, NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (div),
+ NULL);
+
+ paragraphs = webkit_dom_element_query_selector_all (
+ div, "#-x-evo-input-start", NULL);
+
+ length = webkit_dom_node_list_get_length (paragraphs);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *paragraph;
+
+ paragraph = webkit_dom_node_list_item (paragraphs, ii);
+
+ webkit_dom_element_set_id (
+ WEBKIT_DOM_ELEMENT (paragraph), "");
+ }
+
+ convert_element_from_html_to_plain_text (
+ view, div, &wrap, &quote);
+
+ source = WEBKIT_DOM_NODE (div);
+
+ clean = TRUE;
+ }
+ }
+
+ paragraphs = webkit_dom_element_query_selector_all (
+ WEBKIT_DOM_ELEMENT (source), ".-x-evo-paragraph", NULL);
+
+ length = webkit_dom_node_list_get_length (paragraphs);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *paragraph;
+
+ paragraph = webkit_dom_node_list_item (paragraphs, ii);
+
+ if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (paragraph) ||
+ WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (paragraph)) {
+ WebKitDOMNode *item = webkit_dom_node_get_first_child (paragraph);
+
+ while (item) {
+ WebKitDOMNode *next_item =
+ webkit_dom_node_get_next_sibling (item);
+
+ if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+ e_html_editor_selection_wrap_paragraph (
+ e_html_editor_view_get_selection (view),
+ WEBKIT_DOM_ELEMENT (item));
+ }
+ item = next_item;
+ }
+ } else {
+ e_html_editor_selection_wrap_paragraph (
+ e_html_editor_view_get_selection (view),
+ WEBKIT_DOM_ELEMENT (paragraph));
+ }
+ }
+
+ paragraphs = webkit_dom_element_query_selector_all (
+ WEBKIT_DOM_ELEMENT (source),
+ "span[id^=\"-x-evo-selection-\"], span#-x-evo-caret-position",
+ NULL);
+
+ length = webkit_dom_node_list_get_length (paragraphs);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii);
+ WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+ webkit_dom_node_remove_child (parent, node, NULL);
+ webkit_dom_node_normalize (parent);
+ }
+
+ if (view->priv->html_mode || quote)
+ quote_plain_text_recursive (document, source, source, 0);
+
+ process_elements (view, source, FALSE, FALSE, TRUE, plain_text);
+
+ if (clean)
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (body), source, NULL);
+
+ /* Return text content between <body> and </body> */
+ return g_string_free (plain_text, FALSE);
+}
+
+static gchar *
+process_content_for_html (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNode *body;
+ WebKitDOMElement *element;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+ process_elements (view, body, TRUE, FALSE, FALSE, NULL);
+ element = webkit_dom_document_get_document_element (document);
+ return webkit_dom_html_element_get_outer_html (
+ WEBKIT_DOM_HTML_ELEMENT (element));
+}
+
+static gboolean
+show_lose_formatting_dialog (EHTMLEditorView *view)
+{
+ gint result;
+ GtkWidget *toplevel, *dialog;
+ GtkWindow *parent = NULL;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+
+ if (GTK_IS_WINDOW (toplevel))
+ parent = GTK_WINDOW (toplevel);
+
+ dialog = gtk_message_dialog_new (
+ parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ _("Turning HTML mode off will cause the text "
+ "to lose all formatting. Do you want to continue?"));
+ gtk_dialog_add_buttons (
+ GTK_DIALOG (dialog),
+ _("_Don't lose formatting"), GTK_RESPONSE_CANCEL,
+ _("_Lose formatting"), GTK_RESPONSE_OK,
+ NULL);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (result != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ /* Nothing has changed, but notify anyway */
+ g_object_notify (G_OBJECT (view), "html-mode");
+ return FALSE;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return TRUE;
+}
+
+static void
+clear_attributes (WebKitDOMDocument *document)
+{
+ gint length, ii;
+ WebKitDOMNamedNodeMap *attributes;
+ WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+ WebKitDOMHTMLHeadElement *head = webkit_dom_document_get_head (document);
+ WebKitDOMElement *document_element =
+ webkit_dom_document_get_document_element (document);
+
+ /* Remove all attributes from HTML element */
+ attributes = webkit_dom_element_get_attributes (document_element);
+ length = webkit_dom_named_node_map_get_length (attributes);
+ for (ii = length - 1; ii >= 0; ii--) {
+ WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+ webkit_dom_element_remove_attribute_node (
+ document_element, WEBKIT_DOM_ATTR (node), NULL);
+ }
+
+ /* Remove everything from HEAD element */
+ while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (head))) {
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (head),
+ webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head)),
+ NULL);
+ }
+
+ /* Remove non Evolution attributes from BODY element */
+ attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+ length = webkit_dom_named_node_map_get_length (attributes);
+ for (ii = length - 1; ii >= 0; ii--) {
+ gchar *name;
+ WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+ name = webkit_dom_node_get_local_name (node);
+
+ if (!g_str_has_prefix (name, "data-") ||
+ g_str_has_prefix (name, "data-inline") ||
+ g_str_has_prefix (name, "data-name")) {
+ webkit_dom_element_remove_attribute_node (
+ WEBKIT_DOM_ELEMENT (body),
+ WEBKIT_DOM_ATTR (node),
+ NULL);
+ }
+
+ g_free (name);
+ }
+}
+
+static void
+convert_when_changing_composer_mode (EHTMLEditorView *view)
+{
+ EHTMLEditorSelection *selection;
+ gboolean quote = FALSE, wrap = FALSE;
+ WebKitDOMDocument *document;
+ WebKitDOMHTMLElement *body;
+
+ selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ convert_element_from_html_to_plain_text (
+ view, WEBKIT_DOM_ELEMENT (body), &wrap, &quote);
+
+ if (wrap)
+ e_html_editor_selection_wrap_paragraphs_in_document (selection, document);
+
+ if (quote) {
+ e_html_editor_selection_save_caret_position (selection);
+ body = WEBKIT_DOM_HTML_ELEMENT (e_html_editor_view_quote_plain_text (view));
+ e_html_editor_selection_restore_caret_position (selection);
+ }
+
+ clear_attributes (document);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL);
+
+ /* Update fonts - in plain text we only want monospace */
+ e_html_editor_view_update_fonts (view);
+
+ e_html_editor_view_force_spell_check (view);
+}
+
+/**
+ * e_html_editor_view_set_html_mode:
+ * @view: an #EHTMLEditorView
+ * @html_mode: @TRUE to enable HTML mode, @FALSE to enable plain text mode
+ *
+ * When switching from HTML to plain text mode, user will be prompted whether
+ * he/she really wants to switch the mode and lose all formatting. When user
+ * declines, the property is not changed. When they accept, the all formatting
+ * is lost.
+ */
+void
+e_html_editor_view_set_html_mode (EHTMLEditorView *view,
+ gboolean html_mode)
+{
+ EHTMLEditorSelection *selection;
+ gboolean is_from_new_message, converted, edit_as_new, message, convert;
+ gboolean reply, hide;
+ WebKitDOMElement *blockquote;
+ WebKitDOMHTMLElement *body;
+ WebKitDOMDocument *document;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ selection = e_html_editor_view_get_selection (view);
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ body = webkit_dom_document_get_body (document);
+
+ is_from_new_message = webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-new-message");
+ converted = webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-converted");
+ edit_as_new = webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-edit-as-new");
+ message = webkit_dom_element_has_attribute (
+ WEBKIT_DOM_ELEMENT (body), "data-message");
+
+ reply = !is_from_new_message && !edit_as_new && message;
+ hide = !reply && !converted;
+
+ convert = message && ((!hide && reply && !converted) || (edit_as_new && !converted));
+
+ /* If toggling from HTML to plain text mode, ask user first */
+ if (convert && view->priv->html_mode && !html_mode) {
+ if (!show_lose_formatting_dialog (view))
+ return;
+
+ view->priv->html_mode = html_mode;
+
+ convert_when_changing_composer_mode (view);
+
+ goto out;
+ }
+
+ if (html_mode == view->priv->html_mode)
+ return;
+
+ view->priv->html_mode = html_mode;
+
+ /* Update fonts - in plain text we only want monospace */
+ e_html_editor_view_update_fonts (view);
+
+ blockquote = webkit_dom_document_query_selector (
+ document, "blockquote[type|=cite]", NULL);
+
+ if (view->priv->html_mode) {
+ if (blockquote)
+ e_html_editor_view_dequote_plain_text (view);
+
+ toggle_paragraphs_style (view);
+ toggle_smileys (view);
+ remove_wrapping (view);
+ } else {
+ gchar *plain;
+
+ /* Save caret position -> it will be restored in e-composer-private.c */
+ e_html_editor_selection_save_caret_position (selection);
+
+ if (blockquote) {
+ e_html_editor_selection_wrap_paragraphs_in_document (
+ selection, document);
+ e_html_editor_view_quote_plain_text (view);
+ }
+
+ toggle_paragraphs_style (view);
+ toggle_smileys (view);
+ remove_images (view);
+
+ plain = process_content_for_mode_change (view);
+
+ if (*plain)
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (view), plain, NULL, NULL, "file://");
+
+ g_free (plain);
+ }
+
+ out:
+ g_object_notify (G_OBJECT (view), "html-mode");
+}
+
+/**
+ * e_html_editor_view_get_inline_spelling:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic spellchecking is enabled or not. When enabled,
+ * editor will perform spellchecking as user is typing. Otherwise spellcheck
+ * has to be run manually from menu.
+ *
+ * Returns: @TRUE when automatic spellchecking is enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_inline_spelling (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+ return view->priv->inline_spelling;
+}
+
+/**
+ * e_html_editor_view_set_inline_spelling:
+ * @view: an #EHTMLEditorView
+ * @inline_spelling: @TRUE to enable automatic spellchecking, @FALSE otherwise
+ *
+ * Enables or disables automatic spellchecking.
+ */
+void
+e_html_editor_view_set_inline_spelling (EHTMLEditorView *view,
+ gboolean inline_spelling)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ if (view->priv->inline_spelling == inline_spelling)
+ return;
+
+ view->priv->inline_spelling = inline_spelling;
+
+ g_object_notify (G_OBJECT (view), "inline-spelling");
+}
+
+/**
+ * e_html_editor_view_get_magic_links:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic links conversion is enabled. When enabled, the editor
+ * will automatically convert any HTTP links into clickable HTML links.
+ *
+ * Returns: @TRUE when magic links are enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_magic_links (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+ return view->priv->magic_links;
+}
+
+/**
+ * e_html_editor_view_set_magic_links:
+ * @view: an #EHTMLEditorView
+ * @magic_links: @TRUE to enable magic links, @FALSE to disable them
+ *
+ * Enables or disables automatic links conversion.
+ */
+void
+e_html_editor_view_set_magic_links (EHTMLEditorView *view,
+ gboolean magic_links)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ if (view->priv->magic_links == magic_links)
+ return;
+
+ view->priv->magic_links = magic_links;
+
+ g_object_notify (G_OBJECT (view), "magic-links");
+}
+
+/**
+ * e_html_editor_view_get_magic_smileys:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic conversion of smileys is enabled or disabled. When
+ * enabled, the editor will automatically convert text smileys ( :-), ;-),...)
+ * into images.
+ *
+ * Returns: @TRUE when magic smileys are enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_magic_smileys (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+ return view->priv->magic_smileys;
+}
+
+/**
+ * e_html_editor_view_set_magic_smileys:
+ * @view: an #EHTMLEditorView
+ * @magic_smileys: @TRUE to enable magic smileys, @FALSE to disable them
+ *
+ * Enables or disables magic smileys.
+ */
+void
+e_html_editor_view_set_magic_smileys (EHTMLEditorView *view,
+ gboolean magic_smileys)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ if (view->priv->magic_smileys == magic_smileys)
+ return;
+
+ view->priv->magic_smileys = magic_smileys;
+
+ g_object_notify (G_OBJECT (view), "magic-smileys");
+}
+
+/**
+ * e_html_editor_view_get_spell_checker:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns an #ESpellChecker object that is used to perform spellchecking.
+ *
+ * Returns: An always-valid #ESpellChecker object
+ */
+ESpellChecker *
+e_html_editor_view_get_spell_checker (EHTMLEditorView *view)
+{
+ return E_SPELL_CHECKER (webkit_get_text_checker ());
+}
+
+/**
+ * e_html_editor_view_get_text_html:
+ * @view: an #EHTMLEditorView:
+ *
+ * Returns processed HTML content of the editor document (with elements attributes
+ * used in Evolution composer)
+ *
+ * Returns: A newly allocated string
+ */
+gchar *
+e_html_editor_view_get_text_html (EHTMLEditorView *view)
+{
+ return process_content_for_html (view);
+}
+
+/**
+ * e_html_editor_view_get_text_html_for_drafts:
+ * @view: an #EHTMLEditorView:
+ *
+ * Returns HTML content of the editor document (without elements attributes
+ * used in Evolution composer)
+ *
+ * Returns: A newly allocated string
+ */
+gchar *
+e_html_editor_view_get_text_html_for_drafts (EHTMLEditorView *view)
+{
+ return process_content_for_saving_as_draft (view);
+}
+
+/**
+ * e_html_editor_view_get_text_plain:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns plain text content of the @view. The algorithm removes any
+ * formatting or styles from the document and keeps only the text and line
+ * breaks.
+ *
+ * Returns: A newly allocated string with plain text content of the document.
+ */
+gchar *
+e_html_editor_view_get_text_plain (EHTMLEditorView *view)
+{
+ return process_content_for_plain_text (view);
+}
+
+static void
+convert_and_load_html_to_plain_text (EHTMLEditorView *view,
+ const gchar *html)
+{
+ view->priv->convertor_insert = FALSE;
+
+ webkit_web_view_load_string (
+ view->priv->convertor_web_view, html, NULL, NULL, "file://");
+}
+
+void
+e_html_editor_view_convert_and_insert_html_to_plain_text (EHTMLEditorView *view,
+ const gchar *html)
+{
+ view->priv->convertor_insert = TRUE;
+
+ webkit_web_view_load_string (
+ view->priv->convertor_web_view, html, NULL, NULL, "file://");
+}
+
+/**
+ * e_html_editor_view_set_text_html:
+ * @view: an #EHTMLEditorView
+ * @text: HTML code to load into the editor
+ *
+ * Loads given @text into the editor, destroying any content already present.
+ */
+void
+e_html_editor_view_set_text_html (EHTMLEditorView *view,
+ const gchar *text)
+{
+ view->priv->reload_in_progress = TRUE;
+
+ /* Only convert messages that are in HTML */
+ if (!view->priv->html_mode && *text && !strstr (text, "data-evo-draft")) {
+ if (strstr (text, "<!-- text/html -->")) {
+ if (!show_lose_formatting_dialog (view)) {
+ e_html_editor_view_set_html_mode (view, TRUE);
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://");
+ return;
+ }
+ convert_and_load_html_to_plain_text (view, text);
+ } else {
+ convert_and_load_html_to_plain_text (view, text);
+ }
+ } else {
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://");
+ }
+}
+
+/**
+ * e_html_editor_view_set_text_plain:
+ * @view: an #EHTMLEditorView
+ * @text: A plain text to load into the editor
+ *
+ * Loads given @text into the editor, destryoing any content already present.
+ */
+void
+e_html_editor_view_set_text_plain (EHTMLEditorView *view,
+ const gchar *text)
+{
+ view->priv->reload_in_progress = TRUE;
+
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (view), text, "text/plain", NULL, "file://");
+}
+
+/**
+ * e_html_editor_view_paste_clipboard_quoted:
+ * @view: an #EHTMLEditorView
+ *
+ * Pastes current content of clipboard into the editor as quoted text
+ */
+void
+e_html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view)
+{
+ EHTMLEditorViewClass *class;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ class = E_HTML_EDITOR_VIEW_GET_CLASS (view);
+ g_return_if_fail (class->paste_clipboard_quoted != NULL);
+
+ class->paste_clipboard_quoted (view);
+}
+
+void
+e_html_editor_view_embed_styles (EHTMLEditorView *view)
+{
+ WebKitWebSettings *settings;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *sheet;
+ gchar *stylesheet_uri;
+ gchar *stylesheet_content;
+ const gchar *stylesheet;
+ gsize length;
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+ g_object_get (
+ G_OBJECT (settings),
+ "user-stylesheet-uri", &stylesheet_uri,
+ NULL);
+
+ stylesheet = strstr (stylesheet_uri, ",");
+ stylesheet_content = (gchar *) g_base64_decode (stylesheet, &length);
+ g_free (stylesheet_uri);
+
+ if (length == 0) {
+ g_free (stylesheet_content);
+ return;
+ }
+
+ e_web_view_create_and_add_css_style_sheet (document, "-x-evo-composer-sheet");
+
+ sheet = webkit_dom_document_get_element_by_id (document, "-x-evo-composer-sheet");
+ webkit_dom_element_set_attribute (
+ sheet,
+ "type",
+ "text/css",
+ NULL);
+
+ webkit_dom_html_element_set_inner_html (WEBKIT_DOM_HTML_ELEMENT (sheet), stylesheet_content, NULL);
+
+ g_free (stylesheet_content);
+}
+
+void
+e_html_editor_view_remove_embed_styles (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMElement *sheet;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ sheet = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-composer-sheet");
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (sheet)),
+ WEBKIT_DOM_NODE (sheet),
+ NULL);
+}
+
+static const gchar *
+citation_color_level_1 (void)
+{
+ return "rgb(114,159,207)"; /* Sky Blue 1 */
+}
+
+static const gchar *
+citation_color_level_2 (void)
+{
+ return "rgb(173,127,168)"; /* Plum 1 */
+}
+
+static const gchar *
+citation_color_level_3 (void)
+{
+ return "rgb(138,226,52)"; /* Chameleon 1 */
+}
+
+static const gchar *
+citation_color_level_4 (void)
+{
+ return "rgb(252,175,62)"; /* Orange 1 */
+}
+
+static const gchar *
+citation_color_level_5 (void)
+{
+ return "rgb(233,185,110)"; /* Chocolate 1 */
+}
+
+/**
+ * e_html_editor_view_update_fonts:
+ * @view: an #EHTMLEditorView
+ *
+ * Forces the editor to reload font settings from WebKitWebSettings and apply
+ * it on the content of the editor document.
+ */
+void
+e_html_editor_view_update_fonts (EHTMLEditorView *view)
+{
+ GString *stylesheet;
+ gchar *base64;
+ gchar *aa = NULL;
+ WebKitWebSettings *settings;
+ PangoFontDescription *ms, *vw;
+ const gchar *styles[] = { "normal", "oblique", "italic" };
+ const gchar *smoothing = NULL;
+ GtkStyleContext *context;
+ GdkColor *link = NULL;
+ GdkColor *visited = NULL;
+ gchar *font;
+
+ font = g_settings_get_string (
+ view->priv->font_settings,
+ "monospace-font-name");
+ ms = pango_font_description_from_string (
+ font ? font : "monospace 10");
+ g_free (font);
+
+ if (view->priv->html_mode) {
+ font = g_settings_get_string (
+ view->priv->font_settings,
+ "font-name");
+ vw = pango_font_description_from_string (
+ font ? font : "serif 10");
+ g_free (font);
+ } else {
+ /* When in plain text mode, force monospace font */
+ vw = pango_font_description_copy (ms);
+ }
+
+ stylesheet = g_string_new ("");
+ g_string_append_printf (
+ stylesheet,
+ "body {\n"
+ " font-family: '%s';\n"
+ " font-size: %dpt;\n"
+ " font-weight: %d;\n"
+ " font-style: %s;\n",
+ pango_font_description_get_family (vw),
+ pango_font_description_get_size (vw) / PANGO_SCALE,
+ pango_font_description_get_weight (vw),
+ styles[pango_font_description_get_style (vw)]);
+
+ if (view->priv->aliasing_settings != NULL)
+ aa = g_settings_get_string (
+ view->priv->aliasing_settings, "antialiasing");
+
+ if (g_strcmp0 (aa, "none") == 0)
+ smoothing = "none";
+ else if (g_strcmp0 (aa, "grayscale") == 0)
+ smoothing = "antialiased";
+ else if (g_strcmp0 (aa, "rgba") == 0)
+ smoothing = "subpixel-antialiased";
+
+ if (smoothing != NULL)
+ g_string_append_printf (
+ stylesheet,
+ " -webkit-font-smoothing: %s;\n",
+ smoothing);
+
+ g_free (aa);
+
+ g_string_append (stylesheet, "}\n");
+
+ g_string_append_printf (
+ stylesheet,
+ "pre,code,.pre {\n"
+ " font-family: '%s';\n"
+ " font-size: %dpt;\n"
+ " font-weight: %d;\n"
+ " font-style: %s;\n"
+ "}",
+ pango_font_description_get_family (ms),
+ pango_font_description_get_size (ms) / PANGO_SCALE,
+ pango_font_description_get_weight (ms),
+ styles[pango_font_description_get_style (ms)]);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (view));
+ gtk_style_context_get_style (
+ context,
+ "link-color", &link,
+ "visited-link-color", &visited,
+ NULL);
+
+ if (link == NULL) {
+ link = g_slice_new0 (GdkColor);
+ link->blue = G_MAXINT16;
+ }
+
+ if (visited == NULL) {
+ visited = g_slice_new0 (GdkColor);
+ visited->red = G_MAXINT16;
+ }
+
+ g_string_append_printf (
+ stylesheet,
+ "a {\n"
+ " color: #%06x;\n"
+ "}\n"
+ "a:visited {\n"
+ " color: #%06x;\n"
+ "}\n",
+ e_color_to_value (link),
+ e_color_to_value (visited));
+
+ /* See bug #689777 for details */
+ g_string_append (
+ stylesheet,
+ "p,pre,code,address {\n"
+ " margin: 0;\n"
+ "}\n"
+ "h1,h2,h3,h4,h5,h6 {\n"
+ " margin-top: 0.2em;\n"
+ " margin-bottom: 0.2em;\n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "img "
+ "{\n"
+ " height: inherit; \n"
+ " width: inherit; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "span.-x-evo-resizable-wrapper "
+ "{\n"
+ " resize: both; \n"
+ " overflow: hidden; \n"
+ " display: inline-block; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "span.-x-evo-resizable-wrapper:hover "
+ "{\n"
+ " outline: 1px dashed red; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "ul,ol "
+ "{\n"
+ " -webkit-padding-start: 7ch; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ ".-x-evo-list-item-alignt-left "
+ "{\n"
+ " text-align: left; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ ".-x-evo-list-item-align-center "
+ "{\n"
+ " text-align: center; \n"
+ " -webkit-padding-start: 0ch; \n"
+ " margin-left: -3ch; \n"
+ " margin-right: 1ch; \n"
+ " list-style-position: inside; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ ".-x-evo-list-item-align-right "
+ "{\n"
+ " text-align: right; \n"
+ " -webkit-padding-start: 0ch; \n"
+ " margin-left: -3ch; \n"
+ " margin-right: 1ch; \n"
+ " list-style-position: inside; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "ol,ul "
+ "{\n"
+ " -webkit-margin-before: 0em; \n"
+ " -webkit-margin-after: 0em; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "blockquote "
+ "{\n"
+ " -webkit-margin-before: 0em; \n"
+ " -webkit-margin-after: 0em; \n"
+ "}\n");
+
+ g_string_append (
+ stylesheet,
+ "blockquote[type=cite] "
+ "{\n"
+ " padding: 0.0ex 0ex;\n"
+ " margin: 0ex;\n"
+ " -webkit-margin-start: 0em; \n"
+ " -webkit-margin-end : 0em; \n"
+ " color: #737373 !important;\n"
+ "}\n");
+
+ g_string_append_printf (
+ stylesheet,
+ ".quote_character "
+ "{\n"
+ " color: %s;\n"
+ "}\n",
+ citation_color_level_1 ());
+
+ g_string_append_printf (
+ stylesheet,
+ ".quote_character+"
+ ".quote_character"
+ "{\n"
+ " color: %s;\n"
+ "}\n",
+ citation_color_level_2 ());
+
+ g_string_append_printf (
+ stylesheet,
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character"
+ "{\n"
+ " color: %s;\n"
+ "}\n",
+ citation_color_level_3 ());
+
+ g_string_append_printf (
+ stylesheet,
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character"
+ "{\n"
+ " color: %s;\n"
+ "}\n",
+ citation_color_level_4 ());
+
+ g_string_append_printf (
+ stylesheet,
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character+"
+ ".quote_character"
+ "{\n"
+ " color: %s;\n"
+ "}\n",
+ citation_color_level_5 ());
+
+ g_string_append (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " padding: 0.4ex 1ex;\n"
+ " margin: 1ex;\n"
+ " border-width: 0px 2px 0px 2px;\n"
+ " border-style: none solid none solid;\n"
+ " border-radius: 2px;\n"
+ "}\n");
+
+ /* Block quote border colors are borrowed from Thunderbird. */
+
+ g_string_append_printf (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " border-color: %s;\n"
+ "}\n",
+ citation_color_level_1 ());
+
+ g_string_append_printf (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " border-color: %s;\n"
+ "}\n",
+ citation_color_level_2 ());
+
+ g_string_append_printf (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " border-color: %s;\n"
+ "}\n",
+ citation_color_level_3 ());
+
+ g_string_append_printf (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " border-color: %s;\n"
+ "}\n",
+ citation_color_level_4 ());
+
+ g_string_append_printf (
+ stylesheet,
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+ "{\n"
+ " border-color: %s;\n"
+ "}\n",
+ citation_color_level_5 ());
+
+ gdk_color_free (link);
+ gdk_color_free (visited);
+
+ base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len);
+ g_string_free (stylesheet, TRUE);
+
+ stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,");
+ g_string_append (stylesheet, base64);
+ g_free (base64);
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+ g_object_set (
+ G_OBJECT (settings),
+ "default-font-size", pango_font_description_get_size (vw) / PANGO_SCALE,
+ "default-font-family", pango_font_description_get_family (vw),
+ "monospace-font-family", pango_font_description_get_family (ms),
+ "default-monospace-font-size", (pango_font_description_get_size (ms) / PANGO_SCALE),
+ "user-stylesheet-uri", stylesheet->str,
+ NULL);
+
+ g_string_free (stylesheet, TRUE);
+
+ pango_font_description_free (ms);
+ pango_font_description_free (vw);
+}
+
+/**
+ * e_html_editor_view_get_element_under_mouse_click:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns DOM element, that was clicked on.
+ *
+ * Returns: DOM element on that was clicked.
+ */
+WebKitDOMElement *
+e_html_editor_view_get_element_under_mouse_click (EHTMLEditorView *view)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+
+ return view->priv->element_under_mouse;
+}
+
+/**
+ * e_html_editor_view_check_magic_links
+ * @view: an #EHTMLEditorView
+ * @include_space: If TRUE the pattern for link expects space on end
+ *
+ * Check if actual selection in given editor is link. If so, it is surrounded
+ * with ANCHOR element.
+ */
+void
+e_html_editor_view_check_magic_links (EHTMLEditorView *view,
+ gboolean include_space)
+{
+ WebKitDOMRange *range;
+
+ g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+ range = html_editor_view_get_dom_range (view);
+ html_editor_view_check_magic_links (view, range, include_space, NULL);
+}
+
+static CamelMimePart *
+e_html_editor_view_add_inline_image_from_element (EHTMLEditorView *view,
+ WebKitDOMElement *element,
+ const gchar *attribute)
+{
+ CamelStream *stream;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *part = NULL;
+ gsize decoded_size;
+ gssize size;
+ gchar *mime_type = NULL;
+ gchar *element_src, *cid, *name;
+ const gchar *base64_encoded_data;
+ guchar *base64_decoded_data;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element)) {
+ return NULL;
+ }
+
+ element_src = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (element), attribute);
+
+ base64_encoded_data = strstr (element_src, ";base64,");
+ if (!base64_encoded_data)
+ goto out;
+
+ mime_type = g_strndup (
+ element_src + 5,
+ base64_encoded_data - (strstr (element_src, "data:") + 5));
+
+ /* Move to actual data */
+ base64_encoded_data += 8;
+
+ base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size);
+
+ stream = camel_stream_mem_new ();
+ size = camel_stream_write (
+ stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL);
+
+ if (size == -1)
+ goto out;
+
+ wrapper = camel_data_wrapper_new ();
+ camel_data_wrapper_construct_from_stream_sync (
+ wrapper, stream, NULL, NULL);
+ g_object_unref (stream);
+
+ camel_data_wrapper_set_mime_type (wrapper, mime_type);
+
+ part = camel_mime_part_new ();
+ camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
+ g_object_unref (wrapper);
+
+ cid = camel_header_msgid_generate ();
+ camel_mime_part_set_content_id (part, cid);
+ name = webkit_dom_element_get_attribute (element, "data-name");
+ camel_mime_part_set_filename (part, name);
+ g_free (name);
+ camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+out:
+ g_free (mime_type);
+ g_free (element_src);
+ g_free (base64_decoded_data);
+
+ return part;
+}
+
+GList *
+e_html_editor_view_get_parts_for_inline_images (EHTMLEditorView *view)
+{
+ GHashTable *added;
+ GList *parts = NULL;
+ gint length, ii;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL);
+
+ length = webkit_dom_node_list_get_length (list);
+ if (length == 0)
+ return parts;
+
+ added = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ for (ii = 0; ii < length; ii++) {
+ CamelMimePart *part;
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ gchar *src = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "src");
+
+ if (!g_hash_table_lookup (added, src)) {
+ part = e_html_editor_view_add_inline_image_from_element (
+ view, WEBKIT_DOM_ELEMENT (node), "src");
+ parts = g_list_append (parts, part);
+ g_hash_table_insert (
+ added, src, (gpointer) camel_mime_part_get_content_id (part));
+ }
+ g_free (src);
+ }
+
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ const gchar *id;
+ gchar *src = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "src");
+
+ if ((id = g_hash_table_lookup (added, src)) != NULL) {
+ gchar *cid = g_strdup_printf ("cid:%s", id);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (node), "src", cid, NULL);
+ g_free (cid);
+ }
+ g_free (src);
+ }
+
+ list = webkit_dom_document_query_selector_all (
+ document, "[data-inline][background]", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ CamelMimePart *part;
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ gchar *src = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "background");
+
+ if (!g_hash_table_lookup (added, src)) {
+ part = e_html_editor_view_add_inline_image_from_element (
+ view, WEBKIT_DOM_ELEMENT (node), "background");
+ if (part) {
+ parts = g_list_append (parts, part);
+ g_hash_table_insert (
+ added, src,
+ (gpointer) camel_mime_part_get_content_id (part));
+ }
+ }
+ g_free (src);
+ }
+
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ gchar *src = webkit_dom_element_get_attribute (
+ WEBKIT_DOM_ELEMENT (node), "background");
+ const gchar *id;
+
+ if ((id = g_hash_table_lookup (added, src)) != NULL) {
+ gchar *cid = g_strdup_printf ("cid:%s", id);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (node), "background", cid, NULL);
+ g_free (cid);
+ }
+ g_free (src);
+ }
+
+ g_hash_table_destroy (added);
+
+ return parts;
+}
+
+/**
+ * e_html_editor_view_add_inline_image_from_mime_part:
+ * @composer: a composer object
+ * @part: a CamelMimePart containing image data
+ *
+ * This adds the mime part @part to @composer as an inline image.
+ **/
+void
+e_html_editor_view_add_inline_image_from_mime_part (EHTMLEditorView *view,
+ CamelMimePart *part)
+{
+ CamelDataWrapper *dw;
+ CamelStream *stream;
+ GByteArray *byte_array;
+ gchar *src, *base64_encoded, *mime_type, *cid_src;
+ const gchar *cid, *name;
+
+ stream = camel_stream_mem_new ();
+ dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+ g_return_if_fail (dw);
+
+ mime_type = camel_data_wrapper_get_mime_type (dw);
+ camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+ camel_stream_close (stream, NULL, NULL);
+
+ byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+ if (!byte_array->data)
+ return;
+
+ base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+ name = camel_mime_part_get_filename (part);
+ /* Insert file name before new src */
+ src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+ cid = camel_mime_part_get_content_id (part);
+ if (!cid) {
+ camel_mime_part_set_content_id (part, NULL);
+ cid = camel_mime_part_get_content_id (part);
+ }
+ cid_src = g_strdup_printf ("cid:%s", cid);
+
+ g_hash_table_insert (view->priv->inline_images, cid_src, src);
+
+ g_free (base64_encoded);
+ g_free (mime_type);
+ g_object_unref (stream);
+}
diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h
new file mode 100644
index 0000000000..e40b15b375
--- /dev/null
+++ b/e-util/e-html-editor-view.h
@@ -0,0 +1,164 @@
+/*
+ * e-html-editor-view.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_VIEW_H
+#define E_HTML_EDITOR_VIEW_H
+
+#include <webkit/webkit.h>
+
+#include <camel/camel.h>
+
+#include <e-util/e-html-editor-selection.h>
+#include <e-util/e-emoticon.h>
+#include <e-util/e-spell-checker.h>
+#include <e-util/e-util-enums.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_VIEW \
+ (e_html_editor_view_get_type ())
+#define E_HTML_EDITOR_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorView))
+#define E_HTML_EDITOR_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass))
+#define E_IS_HTML_EDITOR_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR_VIEW))
+#define E_IS_HTML_EDITOR_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR_VIEW))
+#define E_HTML_EDITOR_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorView EHTMLEditorView;
+typedef struct _EHTMLEditorViewClass EHTMLEditorViewClass;
+typedef struct _EHTMLEditorViewPrivate EHTMLEditorViewPrivate;
+
+struct _EHTMLEditorView {
+ WebKitWebView parent;
+ EHTMLEditorViewPrivate *priv;
+};
+
+struct _EHTMLEditorViewClass {
+ WebKitWebViewClass parent_class;
+
+ void (*paste_clipboard_quoted)
+ (EHTMLEditorView *view);
+ gboolean (*popup_event) (EHTMLEditorView *view,
+ GdkEventButton *event);
+ void (*paste_primary_clipboard)
+ (EHTMLEditorView *view);
+};
+
+GType e_html_editor_view_get_type (void) G_GNUC_CONST;
+EHTMLEditorView *
+ e_html_editor_view_new (void);
+EHTMLEditorSelection *
+ e_html_editor_view_get_selection
+ (EHTMLEditorView *view);
+gboolean e_html_editor_view_exec_command (EHTMLEditorView *view,
+ EHTMLEditorViewCommand command,
+ const gchar *value);
+gboolean e_html_editor_view_get_changed (EHTMLEditorView *view);
+void e_html_editor_view_set_changed (EHTMLEditorView *view,
+ gboolean changed);
+gboolean e_html_editor_view_get_html_mode
+ (EHTMLEditorView *view);
+void e_html_editor_view_set_html_mode
+ (EHTMLEditorView *view,
+ gboolean html_mode);
+gboolean e_html_editor_view_get_inline_spelling
+ (EHTMLEditorView *view);
+void e_html_editor_view_set_inline_spelling
+ (EHTMLEditorView *view,
+ gboolean inline_spelling);
+gboolean e_html_editor_view_get_magic_links
+ (EHTMLEditorView *view);
+void e_html_editor_view_set_magic_links
+ (EHTMLEditorView *view,
+ gboolean magic_links);
+void e_html_editor_view_insert_smiley
+ (EHTMLEditorView *view,
+ EEmoticon *emoticon);
+gboolean e_html_editor_view_get_magic_smileys
+ (EHTMLEditorView *view);
+void e_html_editor_view_set_magic_smileys
+ (EHTMLEditorView *view,
+ gboolean magic_smileys);
+ESpellChecker * e_html_editor_view_get_spell_checker
+ (EHTMLEditorView *view);
+gchar * e_html_editor_view_get_text_html
+ (EHTMLEditorView *view);
+gchar * e_html_editor_view_get_text_html_for_drafts
+ (EHTMLEditorView *view);
+gchar * e_html_editor_view_get_text_plain
+ (EHTMLEditorView *view);
+void e_html_editor_view_convert_and_insert_html_to_plain_text
+ (EHTMLEditorView *view,
+ const gchar *html);
+void e_html_editor_view_set_text_html
+ (EHTMLEditorView *view,
+ const gchar *text);
+void e_html_editor_view_set_text_plain
+ (EHTMLEditorView *view,
+ const gchar *text);
+void e_html_editor_view_paste_clipboard_quoted
+ (EHTMLEditorView *view);
+void e_html_editor_view_embed_styles (EHTMLEditorView *view);
+void e_html_editor_view_remove_embed_styles
+ (EHTMLEditorView *view);
+void e_html_editor_view_update_fonts (EHTMLEditorView *view);
+WebKitDOMElement *
+ e_html_editor_view_get_element_under_mouse_click
+ (EHTMLEditorView *view);
+void e_html_editor_view_check_magic_links
+ (EHTMLEditorView *view,
+ gboolean while_typing);
+WebKitDOMElement *
+ e_html_editor_view_quote_plain_text_element
+ (EHTMLEditorView *view,
+ WebKitDOMElement *element);
+WebKitDOMElement *
+ e_html_editor_view_quote_plain_text
+ (EHTMLEditorView *view);
+void e_html_editor_view_dequote_plain_text
+ (EHTMLEditorView *view);
+void e_html_editor_view_turn_spell_check_off
+ (EHTMLEditorView *view);
+void e_html_editor_view_force_spell_check_for_current_paragraph
+ (EHTMLEditorView *view);
+void e_html_editor_view_force_spell_check
+ (EHTMLEditorView *view);
+void e_html_editor_view_add_inline_image_from_mime_part
+ (EHTMLEditorView *view,
+ CamelMimePart *part);
+GList * e_html_editor_view_get_parts_for_inline_images
+ (EHTMLEditorView *view);
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_VIEW_H */
diff --git a/e-util/e-html-editor.c b/e-util/e-html-editor.c
new file mode 100644
index 0000000000..aeae5373b0
--- /dev/null
+++ b/e-util/e-html-editor.c
@@ -0,0 +1,1178 @@
+/*
+ * e-html-editor.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor.h"
+
+#include "e-activity-bar.h"
+#include "e-alert-bar.h"
+#include "e-alert-dialog.h"
+#include "e-alert-sink.h"
+#include "e-html-editor-private.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-selection.h"
+
+#define E_HTML_EDITOR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_HTML_EDITOR, EHTMLEditorPrivate))
+
+/**
+ * EHTMLEditor:
+ *
+ * #EHTMLEditor provides GUI for manipulating with properties of #EHTMLEditorView and
+ * its #EHTMLEditorSelection - i.e. toolbars and actions.
+ */
+
+/* This controls how spelling suggestions are divided between the primary
+ * context menu and a secondary menu. The idea is to prevent the primary
+ * menu from growing too long.
+ *
+ * The constants below are used as follows:
+ *
+ * if TOTAL_SUGGESTIONS <= MAX_LEVEL1_SUGGETIONS:
+ * LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * elif TOTAL_SUGGESTIONS - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS:
+ * LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * else
+ * LEVEL1_SUGGESTIONS = MAX_LEVEL1_SUGGETIONS
+ *
+ * LEVEL2_SUGGESTIONS = TOTAL_SUGGESTIONS - LEVEL1_SUGGESTIONS
+ *
+ * Note that MAX_LEVEL1_SUGGETIONS is not a hard maximum.
+ */
+#define MAX_LEVEL1_SUGGESTIONS 4
+#define MIN_LEVEL2_SUGGESTIONS 3
+
+enum {
+ PROP_0,
+ PROP_FILENAME
+};
+
+enum {
+ UPDATE_ACTIONS,
+ SPELL_LANGUAGES_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* Forward Declarations */
+static void e_html_editor_alert_sink_init
+ (EAlertSinkInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EHTMLEditor,
+ e_html_editor,
+ GTK_TYPE_GRID,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ALERT_SINK,
+ e_html_editor_alert_sink_init))
+
+/* Action callback for context menu spelling suggestions.
+ * XXX This should really be in e-html-editor-actions.c */
+static void
+action_context_spell_suggest_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ const gchar *word;
+
+ word = g_object_get_data (G_OBJECT (action), "word");
+ g_return_if_fail (word != NULL);
+
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+
+ e_html_editor_selection_replace_caret_word (selection, word);
+}
+
+static void
+html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ WebKitSpellChecker *checker;
+ GtkActionGroup *action_group;
+ GtkUIManager *manager;
+ gchar **suggestions;
+ const gchar *path;
+ gchar *word;
+ guint count = 0;
+ guint length;
+ guint merge_id;
+ guint threshold;
+ gint ii;
+
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+
+ word = e_html_editor_selection_get_caret_word (selection);
+ if (word == NULL || *word == '\0')
+ return;
+
+ suggestions = webkit_spell_checker_get_guesses_for_word (checker, word, NULL);
+
+ path = "/context-menu/context-spell-suggest/";
+ manager = e_html_editor_get_ui_manager (editor);
+ action_group = editor->priv->suggestion_actions;
+ merge_id = editor->priv->spell_suggestions_merge_id;
+
+ length = (suggestions != NULL) ? g_strv_length (suggestions) : 0;
+
+ /* Calculate how many suggestions to put directly in the
+ * context menu. The rest will go in a secondary menu. */
+ if (length <= MAX_LEVEL1_SUGGESTIONS) {
+ threshold = length;
+ } else if (length - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS) {
+ threshold = length;
+ } else {
+ threshold = MAX_LEVEL1_SUGGESTIONS;
+ }
+
+ ii = 0;
+ for (ii = 0; suggestions && suggestions[ii]; ii++) {
+ gchar *suggestion = suggestions[ii];
+ gchar *action_name;
+ gchar *action_label;
+ GtkAction *action;
+ GtkWidget *child;
+ GSList *proxies;
+
+ /* Once we reach the threshold, put all subsequent
+ * spelling suggestions in a secondary menu. */
+ if (count == threshold)
+ path = "/context-menu/context-more-suggestions-menu/";
+
+ /* Action name just needs to be unique. */
+ action_name = g_strdup_printf ("suggest-%d", count++);
+
+ action_label = g_markup_printf_escaped (
+ "<b>%s</b>", suggestion);
+
+ action = gtk_action_new (
+ action_name, action_label, NULL, NULL);
+
+ g_object_set_data_full (
+ G_OBJECT (action), "word",
+ g_strdup (suggestion), g_free);
+
+ g_signal_connect (
+ action, "activate", G_CALLBACK (
+ action_context_spell_suggest_cb), editor);
+
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (
+ manager, merge_id, path,
+ action_name, action_name,
+ GTK_UI_MANAGER_AUTO, FALSE);
+
+ /* XXX GtkAction offers no support for Pango markup,
+ * so we have to manually set "use-markup" on the
+ * child of the proxy widget. */
+ gtk_ui_manager_ensure_update (manager);
+ proxies = gtk_action_get_proxies (action);
+ child = gtk_bin_get_child (proxies->data);
+ g_object_set (child, "use-markup", TRUE, NULL);
+
+ g_free (action_name);
+ g_free (action_label);
+ }
+
+ g_free (word);
+ g_strfreev (suggestions);
+}
+
+/* Helper for html_editor_update_actions() */
+static void
+html_editor_spell_checkers_foreach (EHTMLEditor *editor,
+ const gchar *language_code)
+{
+ EHTMLEditorView *view;
+ EHTMLEditorSelection *selection;
+ ESpellChecker *spell_checker;
+ ESpellDictionary *dictionary;
+ GtkActionGroup *action_group;
+ GtkUIManager *manager;
+ GList *list, *link;
+ gchar *path;
+ gchar *word;
+ gint ii = 0;
+ guint merge_id;
+
+ view = e_html_editor_get_view (editor);
+ selection = e_html_editor_view_get_selection (view);
+ spell_checker = e_html_editor_view_get_spell_checker (view);
+
+ word = e_html_editor_selection_get_caret_word (selection);
+ if (word == NULL || *word == '\0')
+ return;
+
+ dictionary = e_spell_checker_ref_dictionary (
+ spell_checker, language_code);
+ if (dictionary != NULL) {
+ list = e_spell_dictionary_get_suggestions (
+ dictionary, word, -1);
+ g_object_unref (dictionary);
+ } else {
+ list = NULL;
+ }
+
+ manager = e_html_editor_get_ui_manager (editor);
+ action_group = editor->priv->suggestion_actions;
+ merge_id = editor->priv->spell_suggestions_merge_id;
+
+ path = g_strdup_printf (
+ "/context-menu/context-spell-suggest/"
+ "context-spell-suggest-%s-menu", language_code);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ gchar *suggestion = link->data;
+ gchar *action_name;
+ gchar *action_label;
+ GtkAction *action;
+ GtkWidget *child;
+ GSList *proxies;
+
+ /* Action name just needs to be unique. */
+ action_name = g_strdup_printf (
+ "suggest-%s-%d", language_code, ii);
+
+ action_label = g_markup_printf_escaped (
+ "<b>%s</b>", suggestion);
+
+ action = gtk_action_new (
+ action_name, action_label, NULL, NULL);
+
+ g_object_set_data_full (
+ G_OBJECT (action), "word",
+ g_strdup (suggestion), g_free);
+
+ g_signal_connect (
+ action, "activate", G_CALLBACK (
+ action_context_spell_suggest_cb), editor);
+
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (
+ manager, merge_id, path,
+ action_name, action_name,
+ GTK_UI_MANAGER_AUTO, FALSE);
+
+ /* XXX GtkAction offers no supports for Pango markup,
+ * so we have to manually set "use-markup" on the
+ * child of the proxy widget. */
+ gtk_ui_manager_ensure_update (manager);
+ proxies = gtk_action_get_proxies (action);
+ if (proxies && proxies->data) {
+ child = gtk_bin_get_child (proxies->data);
+ g_object_set (child, "use-markup", TRUE, NULL);
+ }
+
+ g_free (action_name);
+ g_free (action_label);
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_free);
+
+ g_free (path);
+ g_free (word);
+}
+
+static void
+html_editor_update_actions (EHTMLEditor *editor,
+ GdkEventButton *event)
+{
+ WebKitWebView *web_view;
+ WebKitSpellChecker *checker;
+ WebKitHitTestResult *hit_test;
+ WebKitHitTestResultContext context;
+ WebKitDOMNode *node;
+ EHTMLEditorSelection *selection;
+ EHTMLEditorView *view;
+ ESpellChecker *spell_checker;
+ GtkUIManager *manager;
+ GtkActionGroup *action_group;
+ GList *list;
+ gchar **languages;
+ guint ii, n_languages;
+ gboolean visible;
+ guint merge_id;
+ gint loc, len;
+
+ view = e_html_editor_get_view (editor);
+ spell_checker = e_html_editor_view_get_spell_checker (view);
+
+ web_view = WEBKIT_WEB_VIEW (view);
+ manager = e_html_editor_get_ui_manager (editor);
+
+ editor->priv->image = NULL;
+ editor->priv->table_cell = NULL;
+
+ /* Update context menu item visibility. */
+ hit_test = webkit_web_view_get_hit_test_result (web_view, event);
+ g_object_get (
+ G_OBJECT (hit_test),
+ "context", &context,
+ "inner-node", &node, NULL);
+ g_object_unref (hit_test);
+
+ visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE);
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
+ if (visible)
+ editor->priv->image = node;
+
+ visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK);
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_LINK), visible);
+
+ visible = (WEBKIT_DOM_IS_HTMLHR_ELEMENT (node));
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_RULE), visible);
+
+ visible = (webkit_dom_node_get_node_type (node) == 3);
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TEXT), visible);
+
+ visible =
+ gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
+ gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
+ gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_TEXT));
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
+
+ /* Set to visible if any of these are true:
+ * - Selection is active and contains a link.
+ * - Cursor is on a link.
+ * - Cursor is on an image that has a URL or target.
+ */
+ visible = (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+ (e_html_editor_dom_node_find_parent_element (node, "A") != NULL));
+ gtk_action_set_visible (ACTION (CONTEXT_REMOVE_LINK), visible);
+
+ visible = (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) ||
+ (e_html_editor_dom_node_find_parent_element (node, "TD") != NULL) ||
+ (e_html_editor_dom_node_find_parent_element (node, "TH") != NULL));
+ gtk_action_set_visible (ACTION (CONTEXT_DELETE_CELL), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_DELETE_COLUMN), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_DELETE_ROW), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_DELETE_TABLE), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_BELOW), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_INSERT_TABLE), visible);
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_CELL), visible);
+ if (visible)
+ editor->priv->table_cell = node;
+
+ /* Note the |= (cursor must be in a table cell). */
+ visible |= (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) ||
+ (e_html_editor_dom_node_find_parent_element (node, "TABLE") != NULL));
+ gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
+
+ /********************** Spell Check Suggestions **********************/
+
+ action_group = editor->priv->suggestion_actions;
+
+ /* Remove the old content from the context menu. */
+ merge_id = editor->priv->spell_suggestions_merge_id;
+ if (merge_id > 0) {
+ gtk_ui_manager_remove_ui (manager, merge_id);
+ editor->priv->spell_suggestions_merge_id = 0;
+ }
+
+ /* Clear the action group for spelling suggestions. */
+ list = gtk_action_group_list_actions (action_group);
+ while (list != NULL) {
+ GtkAction *action = list->data;
+
+ gtk_action_group_remove_action (action_group, action);
+ list = g_list_delete_link (list, list);
+ }
+
+ languages = e_spell_checker_list_active_languages (
+ spell_checker, &n_languages);
+
+ /* Decide if we should show spell checking items. */
+ checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+ selection = e_html_editor_view_get_selection (view);
+ visible = FALSE;
+ if ((n_languages > 0) && e_html_editor_selection_has_text (selection)) {
+ gchar *word = e_html_editor_selection_get_caret_word (selection);
+ if (word && *word) {
+ webkit_spell_checker_check_spelling_of_string (
+ checker, word, &loc, &len);
+ visible = (loc > -1);
+ } else {
+ visible = FALSE;
+ }
+ g_free (word);
+ }
+
+ action_group = editor->priv->spell_check_actions;
+ gtk_action_group_set_visible (action_group, visible);
+
+ /* Exit early if spell checking items are invisible. */
+ if (!visible) {
+ g_strfreev (languages);
+ return;
+ }
+
+ merge_id = gtk_ui_manager_new_merge_id (manager);
+ editor->priv->spell_suggestions_merge_id = merge_id;
+
+ /* Handle a single active language as a special case. */
+ if (n_languages == 1) {
+ html_editor_inline_spelling_suggestions (editor);
+ g_strfreev (languages);
+ return;
+ }
+
+ /* Add actions and context menu content for active languages. */
+ for (ii = 0; ii < n_languages; ii++)
+ html_editor_spell_checkers_foreach (editor, languages[ii]);
+
+ g_strfreev (languages);
+}
+
+static void
+html_editor_spell_languages_changed (EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+ ESpellChecker *spell_checker;
+ WebKitWebSettings *settings;
+ gchar *comma_separated;
+ gchar **languages;
+
+ view = e_html_editor_get_view (editor);
+ spell_checker = e_html_editor_view_get_spell_checker (view);
+
+ languages = e_spell_checker_list_active_languages (spell_checker, NULL);
+ comma_separated = g_strjoinv (",", languages);
+ g_strfreev (languages);
+
+ /* Set the languages for webview to highlight misspelled words */
+ settings = webkit_web_view_get_settings (
+ WEBKIT_WEB_VIEW (editor->priv->html_editor_view));
+
+ g_object_set (
+ G_OBJECT (settings),
+ "spell-checking-languages", comma_separated,
+ NULL);
+
+ if (editor->priv->spell_check_dialog != NULL)
+ e_html_editor_spell_check_dialog_update_dictionaries (
+ E_HTML_EDITOR_SPELL_CHECK_DIALOG (
+ editor->priv->spell_check_dialog));
+
+ if (*comma_separated)
+ e_html_editor_view_force_spell_check (editor->priv->html_editor_view);
+ else
+ e_html_editor_view_turn_spell_check_off (editor->priv->html_editor_view);
+
+ g_free (comma_separated);
+}
+
+static gboolean
+html_editor_show_popup (EHTMLEditor *editor,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkWidget *menu;
+
+ menu = e_html_editor_get_managed_widget (editor, "/context-menu");
+
+ g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, event);
+
+ if (event != NULL)
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, NULL,
+ user_data, event->button, event->time);
+ else
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, NULL,
+ user_data, 0, gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static gchar *
+html_editor_find_ui_file (const gchar *basename)
+{
+ gchar *filename;
+
+ g_return_val_if_fail (basename != NULL, NULL);
+
+ /* Support running directly from the source tree. */
+ filename = g_build_filename (".", basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ /* XXX This is kinda broken. */
+ filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ g_critical ("Could not locate '%s'", basename);
+
+ return NULL;
+}
+
+static void
+html_editor_parent_changed (GtkWidget *widget,
+ GtkWidget *previous_parent)
+{
+ GtkWidget *top_level;
+ EHTMLEditor *editor = E_HTML_EDITOR (widget);
+
+ /* If he now have a window, then install our accelators to it */
+ top_level = gtk_widget_get_toplevel (widget);
+ if (GTK_IS_WINDOW (top_level)) {
+ gtk_window_add_accel_group (
+ GTK_WINDOW (top_level),
+ gtk_ui_manager_get_accel_group (editor->priv->manager));
+ }
+}
+
+static void
+html_editor_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FILENAME:
+ e_html_editor_set_filename (
+ E_HTML_EDITOR (object),
+ g_value_get_string (value));
+ return;
+
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FILENAME:
+ g_value_set_string (
+ value, e_html_editor_get_filename (
+ E_HTML_EDITOR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_constructed (GObject *object)
+{
+ EHTMLEditor *editor = E_HTML_EDITOR (object);
+ EHTMLEditorPrivate *priv = editor->priv;
+ GtkIMMulticontext *im_context;
+
+ GtkWidget *widget;
+ GtkToolbar *toolbar;
+ GtkToolItem *tool_item;
+
+ /* Construct the editing toolbars. */
+
+ widget = e_html_editor_get_managed_widget (editor, "/edit-toolbar");
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+ gtk_grid_attach (GTK_GRID (editor), widget, 0, 0, 1, 1);
+ priv->edit_toolbar = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = e_html_editor_get_managed_widget (editor, "/html-toolbar");
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+ gtk_grid_attach (GTK_GRID (editor), widget, 0, 1, 1, 1);
+ priv->html_toolbar = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Construct the activity bar. */
+
+ widget = e_activity_bar_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (GTK_GRID (editor), widget, 0, 2, 1, 1);
+ priv->activity_bar = g_object_ref (widget);
+
+ /* Construct the alert bar for errors. */
+
+ widget = e_alert_bar_new ();
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_grid_attach (GTK_GRID (editor), widget, 0, 3, 1, 1);
+ priv->alert_bar = g_object_ref (widget);
+ /* EAlertBar controls its own visibility. */
+
+ /* Construct the main editing area. */
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_widget_set_vexpand (widget, TRUE);
+ gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
+ priv->scrolled_window = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = GTK_WIDGET (e_html_editor_get_view (editor));
+ gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget);
+ gtk_widget_show (widget);
+ g_signal_connect_swapped (
+ widget, "popup-event",
+ G_CALLBACK (html_editor_show_popup), editor);
+
+ /* Add some combo boxes to the "edit" toolbar. */
+
+ toolbar = GTK_TOOLBAR (priv->edit_toolbar);
+
+ tool_item = gtk_tool_item_new ();
+ widget = e_action_combo_box_new_with_action (
+ GTK_RADIO_ACTION (ACTION (STYLE_NORMAL)));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_set_tooltip_text (widget, _("Paragraph Style"));
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ priv->style_combo_box = g_object_ref (widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ tool_item = gtk_separator_tool_item_new ();
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ tool_item = gtk_tool_item_new ();
+ widget = e_action_combo_box_new_with_action (
+ GTK_RADIO_ACTION (ACTION (MODE_HTML)));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ priv->mode_combo_box = g_object_ref (widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ /* Add some combo boxes to the "html" toolbar. */
+
+ toolbar = GTK_TOOLBAR (priv->html_toolbar);
+
+ tool_item = gtk_tool_item_new ();
+ widget = e_color_combo_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_set_tooltip_text (widget, _("Font Color"));
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ priv->color_combo_box = g_object_ref (widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+ g_object_bind_property (
+ priv->color_combo_box, "current-color",
+ priv->selection, "font-color",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (
+ priv->html_editor_view, "editable",
+ priv->color_combo_box, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ tool_item = gtk_tool_item_new ();
+ widget = e_action_combo_box_new_with_action (
+ GTK_RADIO_ACTION (ACTION (SIZE_PLUS_ZERO)));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_set_tooltip_text (widget, _("Font Size"));
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ priv->size_combo_box = g_object_ref (widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ /* Add input methods to the context menu. */
+ widget = e_html_editor_get_managed_widget (
+ editor, "/context-menu/context-input-methods-menu");
+ widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ g_object_get (
+ G_OBJECT (priv->html_editor_view), "im-context", &im_context, NULL);
+ gtk_im_multicontext_append_menuitems (
+ GTK_IM_MULTICONTEXT (im_context),
+ GTK_MENU_SHELL (widget));
+}
+
+static void
+html_editor_dispose (GObject *object)
+{
+ EHTMLEditorPrivate *priv;
+
+ priv = E_HTML_EDITOR_GET_PRIVATE (object);
+
+ g_clear_object (&priv->manager);
+ g_clear_object (&priv->core_actions);
+ g_clear_object (&priv->html_actions);
+ g_clear_object (&priv->context_actions);
+ g_clear_object (&priv->html_context_actions);
+ g_clear_object (&priv->language_actions);
+ g_clear_object (&priv->spell_check_actions);
+ g_clear_object (&priv->suggestion_actions);
+
+ g_clear_object (&priv->main_menu);
+ g_clear_object (&priv->main_toolbar);
+ g_clear_object (&priv->edit_toolbar);
+ g_clear_object (&priv->html_toolbar);
+ g_clear_object (&priv->activity_bar);
+ g_clear_object (&priv->alert_bar);
+ g_clear_object (&priv->edit_area);
+
+ g_clear_object (&priv->color_combo_box);
+ g_clear_object (&priv->mode_combo_box);
+ g_clear_object (&priv->size_combo_box);
+ g_clear_object (&priv->style_combo_box);
+ g_clear_object (&priv->scrolled_window);
+
+ g_clear_object (&priv->html_editor_view);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_html_editor_parent_class)->dispose (object);
+}
+
+static void
+html_editor_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ EHTMLEditorPrivate *priv;
+ EAlertBar *alert_bar;
+ GtkWidget *toplevel;
+ GtkWidget *widget;
+ GtkWindow *parent;
+
+ priv = E_HTML_EDITOR_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:
+ widget = GTK_WIDGET (alert_sink);
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (GTK_IS_WINDOW (toplevel))
+ parent = GTK_WINDOW (toplevel);
+ else
+ parent = NULL;
+ widget = e_alert_dialog_new (parent, alert);
+ gtk_dialog_run (GTK_DIALOG (widget));
+ gtk_widget_destroy (widget);
+ }
+}
+
+static void
+e_html_editor_class_init (EHTMLEditorClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EHTMLEditorPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = html_editor_set_property;
+ object_class->get_property = html_editor_get_property;
+ object_class->constructed = html_editor_constructed;
+ object_class->dispose = html_editor_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->parent_set = html_editor_parent_changed;
+
+ class->update_actions = html_editor_update_actions;
+ class->spell_languages_changed = html_editor_spell_languages_changed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILENAME,
+ g_param_spec_string (
+ "filename",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[UPDATE_ACTIONS] = g_signal_new (
+ "update-actions",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EHTMLEditorClass, update_actions),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
+ "spell-languages-changed",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EHTMLEditorClass, spell_languages_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+e_html_editor_alert_sink_init (EAlertSinkInterface *interface)
+{
+ interface->submit_alert = html_editor_submit_alert;
+}
+
+static void
+e_html_editor_init (EHTMLEditor *editor)
+{
+ EHTMLEditorPrivate *priv;
+ GtkWidget *widget;
+ gchar *filename;
+ GError *error = NULL;
+
+ editor->priv = E_HTML_EDITOR_GET_PRIVATE (editor);
+
+ priv = editor->priv;
+
+ priv->manager = gtk_ui_manager_new ();
+ priv->core_actions = gtk_action_group_new ("core");
+ priv->html_actions = gtk_action_group_new ("html");
+ priv->context_actions = gtk_action_group_new ("core-context");
+ priv->html_context_actions = gtk_action_group_new ("html-context");
+ priv->language_actions = gtk_action_group_new ("language");
+ priv->spell_check_actions = gtk_action_group_new ("spell-check");
+ priv->suggestion_actions = gtk_action_group_new ("suggestion");
+ priv->html_editor_view = g_object_ref_sink (e_html_editor_view_new ());
+ priv->selection = e_html_editor_view_get_selection (priv->html_editor_view);
+
+ filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
+ if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
+ g_critical ("Couldn't load builder file: %s\n", error->message);
+ g_clear_error (&error);
+ }
+ g_free (filename);
+
+ editor_actions_init (editor);
+ priv->editor_layout_row = 2;
+
+ /* Tweak the main-toolbar style. */
+ widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+ gtk_style_context_add_class (
+ gtk_widget_get_style_context (widget),
+ GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+}
+
+/**
+ * e_html_editor_new:
+ *
+ * Constructs a new #EHTMLEditor.
+ *
+ * Returns: A newly created widget. [transfer-full]
+ */
+GtkWidget *
+e_html_editor_new (void)
+{
+ return g_object_new (E_TYPE_HTML_EDITOR, NULL);
+}
+
+/**
+ * e_html_editor_get_view:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns instance of #EHTMLEditorView used in the @editor.
+ */
+EHTMLEditorView *
+e_html_editor_get_view (EHTMLEditor *editor)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ return editor->priv->html_editor_view;
+}
+
+/**
+ * e_html_editor_get_ui_manager:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns #GtkUIManager that manages all the actions in the @editor.
+ */
+GtkUIManager *
+e_html_editor_get_ui_manager (EHTMLEditor *editor)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ return editor->priv->manager;
+}
+
+/**
+ * e_html_editor_get_action:
+ * @editor: an #EHTMLEditor
+ * @action_name: name of action to lookup and return
+ *
+ * Returns: A #GtkAction matching @action_name or @NULL if no such action exists.
+ */
+GtkAction *
+e_html_editor_get_action (EHTMLEditor *editor,
+ const gchar *action_name)
+{
+ GtkUIManager *manager;
+ GtkAction *action = NULL;
+ GList *list;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ manager = e_html_editor_get_ui_manager (editor);
+ list = gtk_ui_manager_get_action_groups (manager);
+
+ while (list != NULL && action == NULL) {
+ GtkActionGroup *action_group = list->data;
+
+ action = gtk_action_group_get_action (
+ action_group, action_name);
+
+ list = g_list_next (list);
+ }
+
+ g_return_val_if_fail (action != NULL, NULL);
+
+ return action;
+}
+
+/**
+ * e_html_editor_get_action_group:
+ * @editor: an #EHTMLEditor
+ * @group_name: name of action group to lookup and return
+ *
+ * Returns: A #GtkActionGroup matching @group_name or @NULL if not such action
+ * group exists.
+ */
+GtkActionGroup *
+e_html_editor_get_action_group (EHTMLEditor *editor,
+ const gchar *group_name)
+{
+ GtkUIManager *manager;
+ GList *list;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+ g_return_val_if_fail (group_name != NULL, NULL);
+
+ manager = e_html_editor_get_ui_manager (editor);
+ list = gtk_ui_manager_get_action_groups (manager);
+
+ while (list != NULL) {
+ GtkActionGroup *action_group = list->data;
+ const gchar *name;
+
+ name = gtk_action_group_get_name (action_group);
+ if (strcmp (name, group_name) == 0)
+ return action_group;
+
+ list = g_list_next (list);
+ }
+
+ return NULL;
+}
+
+GtkWidget *
+e_html_editor_get_managed_widget (EHTMLEditor *editor,
+ const gchar *widget_path)
+{
+ GtkUIManager *manager;
+ GtkWidget *widget;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+ g_return_val_if_fail (widget_path != NULL, NULL);
+
+ manager = e_html_editor_get_ui_manager (editor);
+ widget = gtk_ui_manager_get_widget (manager, widget_path);
+
+ g_return_val_if_fail (widget != NULL, NULL);
+
+ return widget;
+}
+
+GtkWidget *
+e_html_editor_get_style_combo_box (EHTMLEditor *editor)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ return editor->priv->style_combo_box;
+}
+
+/**
+ * e_html_editor_get_filename:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns path and name of file to which content of the editor should be saved.
+ */
+const gchar *
+e_html_editor_get_filename (EHTMLEditor *editor)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ return editor->priv->filename;
+}
+
+/**
+ * e_html_editor_set_filename:
+ * @editor: an #EHTMLEditor
+ * @filename: Target file
+ *
+ * Sets file to which content of the editor should be saved (see
+ * e_html_editor_save()).
+ */
+void
+e_html_editor_set_filename (EHTMLEditor *editor,
+ const gchar *filename)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+ if (g_strcmp0 (editor->priv->filename, filename) == 0)
+ return;
+
+ g_free (editor->priv->filename);
+ editor->priv->filename = g_strdup (filename);
+
+ g_object_notify (G_OBJECT (editor), "filename");
+}
+
+EActivityBar *
+e_html_editor_get_activity_bar (EHTMLEditor *editor)
+{
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ return E_ACTIVITY_BAR (editor->priv->activity_bar);
+}
+
+/**
+ * e_html_editor_new_activity:
+ * @editor: an #EHTMLEditor
+ *
+ * Creates and configures a new #EActivity so its progress is shown in
+ * the @editor. The #EActivity comes pre-loaded with a #CamelOperation.
+ *
+ * Returns: a new #EActivity for use with @editor
+ **/
+EActivity *
+e_html_editor_new_activity (EHTMLEditor *editor)
+{
+ EActivity *activity;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+ activity = e_activity_new ();
+ e_activity_set_alert_sink (activity, E_ALERT_SINK (editor));
+
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (activity, cancellable);
+ g_object_unref (cancellable);
+
+ activity_bar = E_ACTIVITY_BAR (editor->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, activity);
+
+ return activity;
+}
+
+/**
+ * e_html_editor_pack_above:
+ * @editor: an #EHTMLEditor
+ * @child: a #GtkWidget
+ *
+ * Inserts @child right between the toolbars and the editor widget itself.
+ */
+void
+e_html_editor_pack_above (EHTMLEditor *editor,
+ GtkWidget *child)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ gtk_grid_insert_row (GTK_GRID (editor), editor->priv->editor_layout_row);
+ gtk_grid_attach (GTK_GRID (editor), child, 0, editor->priv->editor_layout_row, 1, 1);
+ editor->priv->editor_layout_row++;
+}
+
+/**
+ * e_html_editor_save:
+ * @editor: an #EHTMLEditor
+ * @filename: file into which to save the content
+ * @as_html: whether the content should be saved as HTML or plain text
+ * @error:[out] a #GError
+ *
+ * Saves current content of the #EHTMLEditorView into given file. When @as_html
+ * is @FALSE, the content is first converted into plain text.
+ *
+ * Returns: @TRUE when content is succesfully saved, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_save (EHTMLEditor *editor,
+ const gchar *filename,
+ gboolean as_html,
+ GError **error)
+{
+ GFile *file;
+ GFileOutputStream *stream;
+ gchar *content;
+ gsize written;
+
+ file = g_file_new_for_path (filename);
+ stream = g_file_replace (
+ file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
+ if ((error && *error) || !stream)
+ return FALSE;
+
+ if (as_html)
+ content = e_html_editor_view_get_text_html (
+ E_HTML_EDITOR_VIEW (editor));
+ else
+ content = e_html_editor_view_get_text_plain (
+ E_HTML_EDITOR_VIEW (editor));
+
+ if (!content || !*content) {
+ g_set_error (
+ error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to obtain content of editor");
+ return FALSE;
+ }
+
+ g_output_stream_write_all (
+ G_OUTPUT_STREAM (stream), content, strlen (content),
+ &written, NULL, error);
+
+ g_free (content);
+ g_object_unref (stream);
+ g_object_unref (file);
+
+ return TRUE;
+}
+
diff --git a/e-util/e-html-editor.h b/e-util/e-html-editor.h
new file mode 100644
index 0000000000..5618cc8e05
--- /dev/null
+++ b/e-util/e-html-editor.h
@@ -0,0 +1,108 @@
+/*
+ * e-html-editor.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_H
+#define E_HTML_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+#include <e-util/e-activity-bar.h>
+#include <e-util/e-html-editor-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR \
+ (e_html_editor_get_type ())
+#define E_HTML_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HTML_EDITOR, EHTMLEditor))
+#define E_HTML_EDITOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HTML_EDITOR, EHTMLEditorClass))
+#define E_IS_HTML_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HTML_EDITOR))
+#define E_IS_HTML_EDITOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HTML_EDITOR))
+#define E_HTML_EDITOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HTML_EDITOR, EHTMLEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditor EHTMLEditor;
+typedef struct _EHTMLEditorClass EHTMLEditorClass;
+typedef struct _EHTMLEditorPrivate EHTMLEditorPrivate;
+
+struct _EHTMLEditor {
+ GtkGrid parent;
+ EHTMLEditorPrivate *priv;
+};
+
+struct _EHTMLEditorClass {
+ GtkGridClass parent_class;
+
+ void (*update_actions) (EHTMLEditor *editor,
+ GdkEventButton *event);
+ void (*spell_languages_changed)
+ (EHTMLEditor *editor);
+};
+
+GType e_html_editor_get_type (void) G_GNUC_CONST;
+GtkWidget * e_html_editor_new (void);
+EHTMLEditorView *
+ e_html_editor_get_view (EHTMLEditor *editor);
+GtkBuilder * e_html_editor_get_builder (EHTMLEditor *editor);
+GtkUIManager * e_html_editor_get_ui_manager (EHTMLEditor *editor);
+GtkAction * e_html_editor_get_action (EHTMLEditor *editor,
+ const gchar *action_name);
+GtkActionGroup *e_html_editor_get_action_group (EHTMLEditor *editor,
+ const gchar *group_name);
+GtkWidget * e_html_editor_get_widget (EHTMLEditor *editor,
+ const gchar *widget_name);
+GtkWidget * e_html_editor_get_managed_widget
+ (EHTMLEditor *editor,
+ const gchar *widget_path);
+GtkWidget * e_html_editor_get_style_combo_box
+ (EHTMLEditor *editor);
+const gchar * e_html_editor_get_filename (EHTMLEditor *editor);
+void e_html_editor_set_filename (EHTMLEditor *editor,
+ const gchar *filename);
+EActivityBar * e_html_editor_get_activity_bar (EHTMLEditor *editor);
+EActivity * e_html_editor_new_activity (EHTMLEditor *editor);
+void e_html_editor_pack_above (EHTMLEditor *editor,
+ GtkWidget *child);
+
+/*****************************************************************************
+ * High-Level Editing Interface
+ *****************************************************************************/
+
+gboolean e_html_editor_save (EHTMLEditor *editor,
+ const gchar *filename,
+ gboolean as_html,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_H */
diff --git a/e-util/e-image-chooser-dialog.c b/e-util/e-image-chooser-dialog.c
new file mode 100644
index 0000000000..73a6c202cb
--- /dev/null
+++ b/e-util/e-image-chooser-dialog.c
@@ -0,0 +1,223 @@
+/*
+ * e-image-chooser-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-image-chooser-dialog.h"
+
+#define E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogPrivate))
+
+#define PREVIEW_WIDTH 256
+#define PREVIEW_HEIGHT 256
+
+typedef struct _Context Context;
+
+struct _EImageChooserDialogPrivate {
+ GCancellable *cancellable;
+};
+
+struct _Context {
+ GtkFileChooser *file_chooser;
+ GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (
+ EImageChooserDialog,
+ e_image_chooser_dialog,
+ GTK_TYPE_FILE_CHOOSER_DIALOG)
+
+static void
+context_free (Context *context)
+{
+ g_object_unref (context->file_chooser);
+ g_object_unref (context->cancellable);
+
+ g_slice_free (Context, context);
+}
+
+static void
+image_chooser_dialog_read_cb (GFile *preview_file,
+ GAsyncResult *result,
+ Context *context)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *preview_widget;
+ GFileInputStream *input_stream;
+
+ input_stream = g_file_read_finish (preview_file, result, NULL);
+
+ /* FIXME Handle errors better, but remember
+ * to ignore G_IO_ERROR_CANCELLED. */
+ if (input_stream == NULL)
+ goto exit;
+
+ /* XXX This blocks, but GDK-PixBuf offers no asynchronous
+ * alternative and I don't want to deal with making GDK
+ * calls from threads and all the crap that goes with it. */
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (
+ G_INPUT_STREAM (input_stream),
+ PREVIEW_WIDTH, PREVIEW_HEIGHT, TRUE,
+ context->cancellable, NULL);
+
+ preview_widget = gtk_file_chooser_get_preview_widget (
+ context->file_chooser);
+
+ gtk_file_chooser_set_preview_widget_active (
+ context->file_chooser, pixbuf != NULL);
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf);
+
+ if (pixbuf != NULL)
+ g_object_unref (pixbuf);
+
+ g_object_unref (input_stream);
+
+exit:
+ context_free (context);
+}
+
+static void
+image_chooser_dialog_update_preview (GtkFileChooser *file_chooser)
+{
+ EImageChooserDialogPrivate *priv;
+ GtkWidget *preview_widget;
+ GFile *preview_file;
+ Context *context;
+
+ priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (file_chooser);
+ preview_file = gtk_file_chooser_get_preview_file (file_chooser);
+ preview_widget = gtk_file_chooser_get_preview_widget (file_chooser);
+
+ if (priv->cancellable != NULL) {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ gtk_image_clear (GTK_IMAGE (preview_widget));
+ gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE);
+
+ if (preview_file == NULL)
+ return;
+
+ priv->cancellable = g_cancellable_new ();
+
+ context = g_slice_new0 (Context);
+ context->file_chooser = g_object_ref (file_chooser);
+ context->cancellable = g_object_ref (priv->cancellable);
+
+ g_file_read_async (
+ preview_file, G_PRIORITY_LOW,
+ priv->cancellable, (GAsyncReadyCallback)
+ image_chooser_dialog_read_cb, context);
+
+ g_object_unref (preview_file);
+}
+
+static void
+image_chooser_dialog_dispose (GObject *object)
+{
+ EImageChooserDialogPrivate *priv;
+
+ priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ if (priv->cancellable != NULL) {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_image_chooser_dialog_parent_class)->dispose (object);
+}
+
+static void
+image_chooser_dialog_constructed (GObject *object)
+{
+ GtkFileChooser *file_chooser;
+ GtkFileFilter *file_filter;
+
+ file_chooser = GTK_FILE_CHOOSER (object);
+ gtk_file_chooser_set_local_only (file_chooser, FALSE);
+
+ gtk_dialog_add_button (
+ GTK_DIALOG (file_chooser),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (
+ GTK_DIALOG (file_chooser),
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response (
+ GTK_DIALOG (file_chooser), GTK_RESPONSE_ACCEPT);
+
+ file_filter = gtk_file_filter_new ();
+ gtk_file_filter_add_pixbuf_formats (file_filter);
+ gtk_file_chooser_set_filter (file_chooser, file_filter);
+
+ gtk_file_chooser_set_preview_widget (file_chooser, gtk_image_new ());
+}
+
+static void
+e_image_chooser_dialog_class_init (EImageChooserDialogClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (
+ class, sizeof (EImageChooserDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = image_chooser_dialog_dispose;
+ object_class->constructed = image_chooser_dialog_constructed;
+}
+
+static void
+e_image_chooser_dialog_init (EImageChooserDialog *dialog)
+{
+ dialog->priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+ g_signal_connect (
+ dialog, "update-preview",
+ G_CALLBACK (image_chooser_dialog_update_preview), NULL);
+}
+
+GtkWidget *
+e_image_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent)
+{
+ return g_object_new (
+ E_TYPE_IMAGE_CHOOSER_DIALOG,
+ "action", GTK_FILE_CHOOSER_ACTION_OPEN,
+ "title", title,
+ "transient-for", parent, NULL);
+}
+
+GFile *
+e_image_chooser_dialog_run (EImageChooserDialog *dialog)
+{
+ GtkFileChooser *file_chooser;
+
+ g_return_val_if_fail (E_IS_IMAGE_CHOOSER_DIALOG (dialog), NULL);
+
+ file_chooser = GTK_FILE_CHOOSER (dialog);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
+ return NULL;
+
+ return gtk_file_chooser_get_file (file_chooser);
+}
diff --git a/e-util/e-image-chooser-dialog.h b/e-util/e-image-chooser-dialog.h
new file mode 100644
index 0000000000..967fddfbad
--- /dev/null
+++ b/e-util/e-image-chooser-dialog.h
@@ -0,0 +1,74 @@
+/*
+ * e-image-chooser-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_IMAGE_CHOOSER_DIALOG_H
+#define E_IMAGE_CHOOSER_DIALOG_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_IMAGE_CHOOSER_DIALOG \
+ (e_image_chooser_dialog_get_type ())
+#define E_IMAGE_CHOOSER_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialog))
+#define E_IMAGE_CHOOSER_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogClass))
+#define E_IS_IMAGE_CHOOSER_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG))
+#define E_IS_IMAGE_CHOOSER_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_IMAGE_CHOOSER_DIALOG))
+#define E_IMAGE_CHOOSER_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EImageChooserDialog EImageChooserDialog;
+typedef struct _EImageChooserDialogClass EImageChooserDialogClass;
+typedef struct _EImageChooserDialogPrivate EImageChooserDialogPrivate;
+
+struct _EImageChooserDialog {
+ GtkFileChooserDialog parent;
+ EImageChooserDialogPrivate *priv;
+};
+
+struct _EImageChooserDialogClass {
+ GtkFileChooserDialogClass parent_class;
+};
+
+GType e_image_chooser_dialog_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_image_chooser_dialog_new
+ (const gchar *title,
+ GtkWindow *parent);
+GFile * e_image_chooser_dialog_run
+ (EImageChooserDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_IMAGE_CHOOSER_DIALOG_H */
diff --git a/e-util/e-mail-signature-editor.c b/e-util/e-mail-signature-editor.c
index 05c783d448..b5c87635f1 100644
--- a/e-util/e-mail-signature-editor.c
+++ b/e-util/e-mail-signature-editor.c
@@ -17,13 +17,13 @@
#include "e-mail-signature-editor.h"
+#include <config.h>
#include <string.h>
#include <glib/gi18n.h>
-#include "e-alert-bar.h"
#include "e-alert-dialog.h"
#include "e-alert-sink.h"
-#include "e-web-view-gtkhtml.h"
+#include "e-alert-bar.h"
#define E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -32,6 +32,7 @@
typedef struct _AsyncContext AsyncContext;
struct _EMailSignatureEditorPrivate {
+ EHTMLEditor *editor;
GtkActionGroup *action_group;
EFocusTracker *focus_tracker;
GCancellable *cancellable;
@@ -40,7 +41,6 @@ struct _EMailSignatureEditorPrivate {
gchar *original_name;
GtkWidget *entry; /* not referenced */
- GtkWidget *alert_bar; /* not referenced */
};
struct _AsyncContext {
@@ -52,6 +52,7 @@ struct _AsyncContext {
enum {
PROP_0,
+ PROP_EDITOR,
PROP_FOCUS_TRACKER,
PROP_REGISTRY,
PROP_SOURCE
@@ -75,17 +76,10 @@ static const gchar *ui =
" </toolbar>\n"
"</ui>";
-/* Forward Declarations */
-static void e_mail_signature_editor_alert_sink_init
- (EAlertSinkInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
+G_DEFINE_TYPE (
EMailSignatureEditor,
e_mail_signature_editor,
- GTKHTML_TYPE_EDITOR,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_ALERT_SINK,
- e_mail_signature_editor_alert_sink_init))
+ GTK_TYPE_WINDOW)
static void
async_context_free (AsyncContext *async_context)
@@ -106,8 +100,10 @@ mail_signature_editor_loaded_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
ESource *source;
- EMailSignatureEditor *editor;
+ EMailSignatureEditor *window;
ESourceMailSignature *extension;
const gchar *extension_name;
const gchar *mime_type;
@@ -116,7 +112,7 @@ mail_signature_editor_loaded_cb (GObject *object,
GError *error = NULL;
source = E_SOURCE (object);
- editor = E_MAIL_SIGNATURE_EDITOR (user_data);
+ window = E_MAIL_SIGNATURE_EDITOR (user_data);
e_source_mail_signature_load_finish (
source, result, &contents, NULL, &error);
@@ -124,17 +120,17 @@ mail_signature_editor_loaded_cb (GObject *object,
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warn_if_fail (contents == NULL);
- g_object_unref (editor);
+ g_object_unref (window);
g_error_free (error);
return;
} else if (error != NULL) {
g_warn_if_fail (contents == NULL);
e_alert_submit (
- E_ALERT_SINK (editor),
+ E_ALERT_SINK (window),
"widgets:no-load-signature",
error->message, NULL);
- g_object_unref (editor);
+ g_object_unref (window);
g_error_free (error);
return;
}
@@ -147,30 +143,18 @@ mail_signature_editor_loaded_cb (GObject *object,
mime_type = e_source_mail_signature_get_mime_type (extension);
is_html = (g_strcmp0 (mime_type, "text/html") == 0);
- gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (editor), is_html);
+ editor = e_mail_signature_editor_get_editor (window);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_html_mode (view, is_html);
- if (is_html) {
- gtkhtml_editor_insert_html (
- GTKHTML_EDITOR (editor), contents);
- } else {
- gtkhtml_editor_insert_text (
- GTKHTML_EDITOR (editor), contents);
-
- gtkhtml_editor_run_command (
- GTKHTML_EDITOR (editor), "cursor-position-save");
- gtkhtml_editor_run_command (
- GTKHTML_EDITOR (editor), "select-all");
- gtkhtml_editor_run_command (
- GTKHTML_EDITOR (editor), "style-pre");
- gtkhtml_editor_run_command (
- GTKHTML_EDITOR (editor), "unselect-all");
- gtkhtml_editor_run_command (
- GTKHTML_EDITOR (editor), "cursor-position-restore");
- }
+ if (is_html)
+ e_html_editor_view_set_text_html (view, contents);
+ else
+ e_html_editor_view_set_text_plain (view, contents);
g_free (contents);
- g_object_unref (editor);
+ g_object_unref (window);
}
static gboolean
@@ -189,28 +173,33 @@ mail_signature_editor_delete_event_cb (EMailSignatureEditor *editor,
static void
action_close_cb (GtkAction *action,
- EMailSignatureEditor *editor)
+ EMailSignatureEditor *window)
{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
gboolean something_changed = FALSE;
const gchar *original_name;
const gchar *signature_name;
- original_name = editor->priv->original_name;
- signature_name = gtk_entry_get_text (GTK_ENTRY (editor->priv->entry));
+ original_name = window->priv->original_name;
+ signature_name = gtk_entry_get_text (GTK_ENTRY (window->priv->entry));
+
+ editor = e_mail_signature_editor_get_editor (window);
+ view = e_html_editor_get_view (editor);
- something_changed |= gtkhtml_editor_has_undo (GTKHTML_EDITOR (editor));
+ something_changed |= webkit_web_view_can_undo (WEBKIT_WEB_VIEW (view));
something_changed |= (strcmp (signature_name, original_name) != 0);
if (something_changed) {
gint response;
response = e_alert_run_dialog_for_args (
- GTK_WINDOW (editor),
+ GTK_WINDOW (window),
"widgets:ask-signature-changed", NULL);
if (response == GTK_RESPONSE_YES) {
GtkActionGroup *action_group;
- action_group = editor->priv->action_group;
+ action_group = window->priv->action_group;
action = gtk_action_group_get_action (
action_group, "save-and-close");
gtk_action_activate (action);
@@ -219,7 +208,7 @@ action_close_cb (GtkAction *action,
return;
}
- gtk_widget_destroy (GTK_WIDGET (editor));
+ gtk_widget_destroy (GTK_WIDGET (window));
}
static void
@@ -292,8 +281,7 @@ action_save_and_close_cb (GtkAction *action,
/* Only make sure that the 'source-changed' is called,
* thus the preview of the signature is updated on save.
* It is not called when only signature body is changed
- * (and ESource properties are left unchanged).
- */
+ * (and ESource properties are left unchanged). */
g_signal_emit_by_name (registry, "source-changed", source);
gtk_widget_destroy (GTK_WIDGET (editor));
@@ -397,6 +385,13 @@ mail_signature_editor_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
+ case PROP_EDITOR:
+ g_value_set_object (
+ value,
+ e_mail_signature_editor_get_editor (
+ E_MAIL_SIGNATURE_EDITOR (object)));
+ return;
+
case PROP_FOCUS_TRACKER:
g_value_set_object (
value,
@@ -429,6 +424,11 @@ mail_signature_editor_dispose (GObject *object)
priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (object);
+ if (priv->editor != NULL) {
+ g_object_unref (priv->editor);
+ priv->editor = NULL;
+ }
+
if (priv->action_group != NULL) {
g_object_unref (priv->action_group);
priv->action_group = NULL;
@@ -477,16 +477,18 @@ mail_signature_editor_finalize (GObject *object)
static void
mail_signature_editor_constructed (GObject *object)
{
- EMailSignatureEditor *editor;
+ EMailSignatureEditor *window;
GtkActionGroup *action_group;
EFocusTracker *focus_tracker;
- GtkhtmlEditor *gtkhtml_editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GtkUIManager *ui_manager;
GDBusObject *dbus_object;
ESource *source;
GtkAction *action;
GtkWidget *container;
GtkWidget *widget;
+ GtkWidget *hbox;
const gchar *display_name;
GError *error = NULL;
@@ -494,10 +496,11 @@ mail_signature_editor_constructed (GObject *object)
G_OBJECT_CLASS (e_mail_signature_editor_parent_class)->
constructed (object);
- editor = E_MAIL_SIGNATURE_EDITOR (object);
+ window = E_MAIL_SIGNATURE_EDITOR (object);
+ editor = e_mail_signature_editor_get_editor (window);
+ view = e_html_editor_get_view (editor);
- gtkhtml_editor = GTKHTML_EDITOR (editor);
- ui_manager = gtkhtml_editor_get_ui_manager (gtkhtml_editor);
+ ui_manager = e_html_editor_get_ui_manager (editor);
/* Because we are loading from a hard-coded string, there is
* no chance of I/O errors. Failure here implies a malformed
@@ -511,103 +514,102 @@ mail_signature_editor_constructed (GObject *object)
action_group, GETTEXT_PACKAGE);
gtk_action_group_add_actions (
action_group, entries,
- G_N_ELEMENTS (entries), editor);
+ G_N_ELEMENTS (entries), window);
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- editor->priv->action_group = g_object_ref (action_group);
+ window->priv->action_group = g_object_ref (action_group);
/* Hide page properties because it is not inherited in the mail. */
- action = gtkhtml_editor_get_action (gtkhtml_editor, "properties-page");
+ action = e_html_editor_get_action (editor, "properties-page");
gtk_action_set_visible (action, FALSE);
- action = gtkhtml_editor_get_action (
- gtkhtml_editor, "context-properties-page");
+ action = e_html_editor_get_action (editor, "context-properties-page");
gtk_action_set_visible (action, FALSE);
gtk_ui_manager_ensure_update (ui_manager);
- gtk_window_set_title (GTK_WINDOW (editor), _("Edit Signature"));
+ gtk_window_set_title (GTK_WINDOW (window), _("Edit Signature"));
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 440);
- /* Construct the signature name entry. */
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ /* Construct the main menu and toolbar. */
+
+ widget = e_html_editor_get_managed_widget (editor, "/main-menu");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
- container = gtkhtml_editor->vbox;
+ /* Construct the signature name entry. */
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- /* Position 2 should be between the main and style toolbars. */
- gtk_box_reorder_child (GTK_BOX (container), widget, 2);
gtk_widget_show (widget);
- container = widget;
+ hbox = widget;
widget = gtk_entry_new ();
- gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0);
- editor->priv->entry = widget; /* not referenced */
+ gtk_box_pack_end (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ window->priv->entry = widget; /* not referenced */
gtk_widget_show (widget);
widget = gtk_label_new_with_mnemonic (_("_Signature Name:"));
- gtk_label_set_mnemonic_widget (GTK_LABEL (widget), editor->priv->entry);
- gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), window->priv->entry);
+ gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
- g_signal_connect (
- editor, "delete-event",
- G_CALLBACK (mail_signature_editor_delete_event_cb), NULL);
-
- /* Construct the alert bar for errors. */
+ /* Construct the main editing area. */
- container = gtkhtml_editor->vbox;
-
- widget = e_alert_bar_new ();
- gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- /* Position 5 should be between the style toolbar and editing area. */
- gtk_box_reorder_child (GTK_BOX (container), widget, 5);
- editor->priv->alert_bar = widget; /* not referenced */
- /* EAlertBar controls its own visibility. */
+ widget = GTK_WIDGET (editor);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
- /* Configure an EFocusTracker to manage selection actions.
- *
- * XXX GtkhtmlEditor does not manage its own selection actions,
- * which is technically a bug but works in our favor here
- * because it won't cause any conflicts with EFocusTracker. */
+ g_signal_connect (
+ window, "delete-event",
+ G_CALLBACK (mail_signature_editor_delete_event_cb), NULL);
- focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));
+ /* Configure an EFocusTracker to manage selection actions. */
+ focus_tracker = e_focus_tracker_new (GTK_WINDOW (window));
- action = gtkhtml_editor_get_action (gtkhtml_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 (gtkhtml_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 (gtkhtml_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 (gtkhtml_editor, "select-all");
+ action = e_html_editor_get_action (editor, "select-all");
e_focus_tracker_set_select_all_action (focus_tracker, action);
- editor->priv->focus_tracker = focus_tracker;
+ window->priv->focus_tracker = focus_tracker;
- source = e_mail_signature_editor_get_source (editor);
+ source = e_mail_signature_editor_get_source (window);
display_name = e_source_get_display_name (source);
if (display_name == NULL || *display_name == '\0')
display_name = _("Unnamed");
/* Set the entry text before we grab focus. */
- g_free (editor->priv->original_name);
- editor->priv->original_name = g_strdup (display_name);
- gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), display_name);
+ g_free (window->priv->original_name);
+ window->priv->original_name = g_strdup (display_name);
+ gtk_entry_set_text (GTK_ENTRY (window->priv->entry), display_name);
/* Set the focus appropriately. If this is a new signature, draw
* the user's attention to the signature name entry. Otherwise go
* straight to the editing area. */
if (source == NULL) {
- gtk_widget_grab_focus (editor->priv->entry);
+ gtk_widget_grab_focus (window->priv->entry);
} else {
- GtkHTML *html;
-
- html = gtkhtml_editor_get_html (gtkhtml_editor);
- gtk_widget_grab_focus (GTK_WIDGET (html));
+ gtk_widget_grab_focus (GTK_WIDGET (view));
}
/* Load file content only for an existing signature.
@@ -623,72 +625,19 @@ mail_signature_editor_constructed (GObject *object)
G_PRIORITY_DEFAULT,
cancellable,
mail_signature_editor_loaded_cb,
- g_object_ref (editor));
+ g_object_ref (window));
- g_warn_if_fail (editor->priv->cancellable == NULL);
- editor->priv->cancellable = cancellable;
+ g_warn_if_fail (window->priv->cancellable == NULL);
+ window->priv->cancellable = cancellable;
g_object_unref (dbus_object);
}
}
static void
-mail_signature_editor_cut_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_copy_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_paste_clipboard (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_select_all (GtkhtmlEditor *editor)
-{
- /* Do nothing. EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_submit_alert (EAlertSink *alert_sink,
- EAlert *alert)
-{
- EMailSignatureEditorPrivate *priv;
- EAlertBar *alert_bar;
- GtkWidget *dialog;
- GtkWindow *parent;
-
- priv = E_MAIL_SIGNATURE_EDITOR_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 void
e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
{
GObjectClass *object_class;
- GtkhtmlEditorClass *editor_class;
g_type_class_add_private (class, sizeof (EMailSignatureEditorPrivate));
@@ -699,11 +648,16 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
object_class->finalize = mail_signature_editor_finalize;
object_class->constructed = mail_signature_editor_constructed;
- editor_class = GTKHTML_EDITOR_CLASS (class);
- editor_class->cut_clipboard = mail_signature_editor_cut_clipboard;
- editor_class->copy_clipboard = mail_signature_editor_copy_clipboard;
- editor_class->paste_clipboard = mail_signature_editor_paste_clipboard;
- editor_class->select_all = mail_signature_editor_select_all;
+ g_object_class_install_property (
+ object_class,
+ PROP_EDITOR,
+ g_param_spec_object (
+ "editor",
+ NULL,
+ NULL,
+ E_TYPE_HTML_EDITOR,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
@@ -742,15 +696,11 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
}
static void
-e_mail_signature_editor_alert_sink_init (EAlertSinkInterface *iface)
-{
- iface->submit_alert = mail_signature_editor_submit_alert;
-}
-
-static void
e_mail_signature_editor_init (EMailSignatureEditor *editor)
{
editor->priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (editor);
+
+ editor->priv->editor = g_object_ref_sink (e_html_editor_new ());
}
GtkWidget *
@@ -764,11 +714,18 @@ e_mail_signature_editor_new (ESourceRegistry *registry,
return g_object_new (
E_TYPE_MAIL_SIGNATURE_EDITOR,
- "html", e_web_view_gtkhtml_new (),
"registry", registry,
"source", source, NULL);
}
+EHTMLEditor *
+e_mail_signature_editor_get_editor (EMailSignatureEditor *editor)
+{
+ g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor), NULL);
+
+ return editor->priv->editor;
+}
+
EFocusTracker *
e_mail_signature_editor_get_focus_tracker (EMailSignatureEditor *editor)
{
@@ -851,7 +808,7 @@ mail_signature_editor_commit_cb (GObject *object,
}
void
-e_mail_signature_editor_commit (EMailSignatureEditor *editor,
+e_mail_signature_editor_commit (EMailSignatureEditor *window,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -864,23 +821,23 @@ e_mail_signature_editor_commit (EMailSignatureEditor *editor,
const gchar *extension_name;
const gchar *mime_type;
gchar *contents;
- gboolean is_html;
- gsize length;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor));
+ g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (window));
- registry = e_mail_signature_editor_get_registry (editor);
- source = e_mail_signature_editor_get_source (editor);
- is_html = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (editor));
+ registry = e_mail_signature_editor_get_registry (window);
+ source = e_mail_signature_editor_get_source (window);
+
+ editor = e_mail_signature_editor_get_editor (window);
+ view = e_html_editor_get_view (editor);
- if (is_html) {
+ if (e_html_editor_view_get_html_mode (view)) {
mime_type = "text/html";
- contents = gtkhtml_editor_get_text_html (
- GTKHTML_EDITOR (editor), &length);
+ contents = e_html_editor_view_get_text_html (view);
} else {
mime_type = "text/plain";
- contents = gtkhtml_editor_get_text_plain (
- GTKHTML_EDITOR (editor), &length);
+ contents = e_html_editor_view_get_text_plain (view);
}
extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
@@ -890,13 +847,13 @@ e_mail_signature_editor_commit (EMailSignatureEditor *editor,
async_context = g_slice_new0 (AsyncContext);
async_context->source = g_object_ref (source);
async_context->contents = contents; /* takes ownership */
- async_context->length = length;
+ async_context->length = strlen (contents);
if (G_IS_CANCELLABLE (cancellable))
async_context->cancellable = g_object_ref (cancellable);
simple = g_simple_async_result_new (
- G_OBJECT (editor), callback, user_data,
+ G_OBJECT (window), callback, user_data,
e_mail_signature_editor_commit);
g_simple_async_result_set_op_res_gpointer (
diff --git a/e-util/e-mail-signature-editor.h b/e-util/e-mail-signature-editor.h
index 7de6841647..1b8622d5d1 100644
--- a/e-util/e-mail-signature-editor.h
+++ b/e-util/e-mail-signature-editor.h
@@ -22,9 +22,9 @@
#ifndef E_MAIL_SIGNATURE_EDITOR_H
#define E_MAIL_SIGNATURE_EDITOR_H
-#include <gtkhtml-editor.h>
#include <libedataserver/libedataserver.h>
+#include <e-util/e-html-editor.h>
#include <e-util/e-focus-tracker.h>
/* Standard GObject macros */
@@ -53,18 +53,20 @@ typedef struct _EMailSignatureEditorClass EMailSignatureEditorClass;
typedef struct _EMailSignatureEditorPrivate EMailSignatureEditorPrivate;
struct _EMailSignatureEditor {
- GtkhtmlEditor parent;
+ GtkWindow parent;
EMailSignatureEditorPrivate *priv;
};
struct _EMailSignatureEditorClass {
- GtkhtmlEditorClass parent_class;
+ GtkWindowClass parent_class;
};
GType e_mail_signature_editor_get_type
(void) G_GNUC_CONST;
GtkWidget * e_mail_signature_editor_new (ESourceRegistry *registry,
ESource *source);
+EHTMLEditor * e_mail_signature_editor_get_editor
+ (EMailSignatureEditor *editor);
EFocusTracker * e_mail_signature_editor_get_focus_tracker
(EMailSignatureEditor *editor);
ESourceRegistry *
diff --git a/e-util/e-mail-signature-manager.c b/e-util/e-mail-signature-manager.c
index 7f75cb7540..fb34c7eb4c 100644
--- a/e-util/e-mail-signature-manager.c
+++ b/e-util/e-mail-signature-manager.c
@@ -398,15 +398,22 @@ mail_signature_manager_constructed (GObject *object)
static void
mail_signature_manager_add_signature (EMailSignatureManager *manager)
{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
ESourceRegistry *registry;
- GtkWidget *editor;
+ GtkWidget *widget;
registry = e_mail_signature_manager_get_registry (manager);
- editor = e_mail_signature_editor_new (registry, NULL);
- gtkhtml_editor_set_html_mode (
- GTKHTML_EDITOR (editor), manager->priv->prefer_html);
- mail_signature_manager_emit_editor_created (manager, editor);
+ widget = e_mail_signature_editor_new (registry, NULL);
+
+ editor = e_mail_signature_editor_get_editor (
+ E_MAIL_SIGNATURE_EDITOR (widget));
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_html_mode (
+ view, manager->priv->prefer_html);
+
+ mail_signature_manager_emit_editor_created (manager, widget);
gtk_widget_grab_focus (manager->priv->tree_view);
}
@@ -435,6 +442,7 @@ mail_signature_manager_editor_created (EMailSignatureManager *manager,
gtk_window_set_transient_for (GTK_WINDOW (editor), parent);
gtk_window_set_position (GTK_WINDOW (editor), position);
+ gtk_widget_set_size_request (GTK_WIDGET (editor), 450, 300);
gtk_widget_show (GTK_WIDGET (editor));
}
diff --git a/e-util/e-mail-signature-preview.c b/e-util/e-mail-signature-preview.c
index c2eeaa6472..ecf64191b6 100644
--- a/e-util/e-mail-signature-preview.c
+++ b/e-util/e-mail-signature-preview.c
@@ -145,12 +145,16 @@ mail_signature_preview_load_cb (ESource *source,
mime_type = e_source_mail_signature_get_mime_type (extension);
if (g_strcmp0 (mime_type, "text/html") == 0) {
- e_web_view_load_string (E_WEB_VIEW (preview), contents);
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (preview), contents,
+ "text/html", "UTF-8", "file:///");
} else {
gchar *string;
string = g_markup_printf_escaped ("<pre>%s</pre>", contents);
- e_web_view_load_string (E_WEB_VIEW (preview), string);
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (preview), string,
+ "text/html", "UTF-8", "file:///");
g_free (string);
}
diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c
index 510dad39b2..88f6da9599 100644
--- a/e-util/e-misc-utils.c
+++ b/e-util/e-misc-utils.c
@@ -1089,6 +1089,50 @@ e_str_without_underscores (const gchar *string)
return new_string;
}
+/**
+ * e_str_replace_string
+ * @text: the string to replace
+ * @before: the string to be replaced
+ * @after: the string to replaced with
+ *
+ * Replaces every occurrence of the string @before with the string @after in
+ * the string @text and returns a #GString with result that should be freed
+ * with g_string_free().
+ *
+ * Returns: a newly-allocated #GString
+ */
+GString *
+e_str_replace_string (const gchar *text,
+ const gchar *before,
+ const gchar *after)
+{
+ const gchar *p, *next;
+ GString *str;
+ gint find_len;
+
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (before != NULL, NULL);
+ g_return_val_if_fail (*before, NULL);
+
+ find_len = strlen (before);
+ str = g_string_new ("");
+
+ p = text;
+ while (next = strstr (p, before), next) {
+ if (p < next)
+ g_string_append_len (str, p, next - p);
+
+ if (after && *after)
+ g_string_append (str, after);
+
+ p = next + find_len;
+ }
+
+ g_string_append (str, p);
+
+ return str;
+}
+
gint
e_str_compare (gconstpointer x,
gconstpointer y)
diff --git a/e-util/e-misc-utils.h b/e-util/e-misc-utils.h
index ebc860b91e..5810490b4f 100644
--- a/e-util/e-misc-utils.h
+++ b/e-util/e-misc-utils.h
@@ -99,6 +99,9 @@ gchar * e_ascii_dtostr (gchar *buffer,
gdouble d);
gchar * e_str_without_underscores (const gchar *string);
+GString * e_str_replace_string (const gchar *text,
+ const gchar *find,
+ const gchar *replace);
gint e_str_compare (gconstpointer x,
gconstpointer y);
gint e_str_case_compare (gconstpointer x,
diff --git a/e-util/e-name-selector-entry.c b/e-util/e-name-selector-entry.c
index a50c886e9c..5a6519c39b 100644
--- a/e-util/e-name-selector-entry.c
+++ b/e-util/e-name-selector-entry.c
@@ -511,10 +511,10 @@ is_quoted_at (const gchar *string,
gunichar c = g_utf8_get_char (p);
if (c == '"')
- quoted = ~quoted;
+ quoted = !quoted;
}
- return quoted ? TRUE : FALSE;
+ return quoted;
}
static gint
@@ -530,7 +530,7 @@ get_index_at_position (const gchar *string,
gunichar c = g_utf8_get_char (p);
if (c == '"')
- quoted = ~quoted;
+ quoted = !quoted;
else if (c == ',' && !quoted)
n++;
}
diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c
new file mode 100644
index 0000000000..c781672103
--- /dev/null
+++ b/e-util/e-spell-checker.c
@@ -0,0 +1,783 @@
+/*
+ * e-spell-checker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
+
+#include <libebackend/libebackend.h>
+#include <webkit/webkitspellchecker.h>
+#include <pango/pango.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define E_SPELL_CHECKER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_SPELL_CHECKER, ESpellCheckerPrivate))
+
+#define MAX_SUGGESTIONS 10
+
+struct _ESpellCheckerPrivate {
+ EnchantBroker *broker;
+ GHashTable *active_dictionaries;
+ GHashTable *dictionaries_cache;
+ gboolean dictionaries_loaded;
+
+ /* We retain ownership of the EnchantDict's since they
+ * have to be freed through enchant_broker_free_dict()
+ * and we also own the EnchantBroker. */
+ GHashTable *enchant_dicts;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVE_LANGUAGES
+};
+
+/* Forward Declarations */
+static void e_spell_checker_init_webkit_checker
+ (WebKitSpellCheckerInterface *interface);
+
+G_DEFINE_TYPE_EXTENDED (
+ ESpellChecker,
+ e_spell_checker,
+ G_TYPE_OBJECT,
+ 0,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EXTENSIBLE, NULL)
+ G_IMPLEMENT_INTERFACE (
+ WEBKIT_TYPE_SPELL_CHECKER,
+ e_spell_checker_init_webkit_checker))
+
+/**
+ * ESpellChecker:
+ *
+ * #ESpellChecker represents a spellchecker in Evolution. It can be used as a
+ * provider for dictionaries. It also implements #WebKitSpellCheckerInterface,
+ * so it can be set as a default spell-checker to WebKit editors
+ */
+
+static gboolean
+spell_checker_enchant_dicts_foreach_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ EnchantDict *enchant_dict = value;
+ EnchantBroker *enchant_broker = user_data;
+
+ enchant_broker_free_dict (enchant_broker, enchant_dict);
+
+ return TRUE;
+}
+
+static void
+wksc_check_spelling (WebKitSpellChecker *webkit_checker,
+ const gchar *word,
+ gint *misspelling_location,
+ gint *misspelling_length)
+{
+ ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+ GHashTable *active_dictionaries;
+ PangoLanguage *language;
+ PangoLogAttr *attrs;
+ gint length, ii;
+
+ active_dictionaries = checker->priv->active_dictionaries;
+ if (g_hash_table_size (active_dictionaries) == 0)
+ return;
+
+ length = g_utf8_strlen (word, -1);
+
+ language = pango_language_get_default ();
+ attrs = g_new (PangoLogAttr, length + 1);
+
+ pango_get_log_attrs (word, -1, -1, language, attrs, length + 1);
+
+ for (ii = 0; ii < length + 1; ii++) {
+ /* We go through each character until we find an is_word_start,
+ * then we get into an inner loop to find the is_word_end
+ * corresponding */
+ if (attrs[ii].is_word_start) {
+ gboolean word_recognized;
+ gint start = ii;
+ gint end = ii;
+ gint word_length;
+ gchar *cstart;
+ gint bytes;
+ gchar *new_word;
+
+ while (attrs[end].is_word_end < 1)
+ end++;
+
+ word_length = end - start;
+ /* Set the iterator to be at the current word
+ * end, so we don't check characters twice. */
+ ii = end;
+
+ cstart = g_utf8_offset_to_pointer (word, start);
+ bytes = g_utf8_offset_to_pointer (word, end) - cstart;
+ new_word = g_new0 (gchar, bytes + 1);
+
+ g_utf8_strncpy (new_word, cstart, word_length);
+
+ word_recognized = e_spell_checker_check_word (
+ checker, new_word, strlen (new_word));
+
+ if (word_recognized) {
+ if (misspelling_location != NULL)
+ *misspelling_location = -1;
+ if (misspelling_length != NULL)
+ *misspelling_length = 0;
+ } else {
+ if (misspelling_location != NULL)
+ *misspelling_location = start;
+ if (misspelling_length != NULL)
+ *misspelling_length = word_length;
+ }
+
+ g_free (new_word);
+ }
+ }
+
+ g_free (attrs);
+}
+
+static gchar **
+wksc_get_guesses (WebKitSpellChecker *webkit_checker,
+ const gchar *word,
+ const gchar *context)
+{
+ ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+ GHashTable *active_dictionaries;
+ GList *list, *link;
+ gchar ** guesses;
+ gint ii = 0;
+
+ guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1);
+
+ active_dictionaries = checker->priv->active_dictionaries;
+ list = g_hash_table_get_keys (active_dictionaries);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
+ GList *suggestions;
+
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ suggestions = e_spell_dictionary_get_suggestions (
+ dictionary, word, -1);
+
+ while (suggestions != NULL && ii < MAX_SUGGESTIONS) {
+ guesses[ii++] = suggestions->data;
+ suggestions->data = NULL;
+
+ suggestions = g_list_delete_link (
+ suggestions, suggestions);
+ }
+
+ g_list_free_full (suggestions, (GDestroyNotify) g_free);
+
+ if (ii >= MAX_SUGGESTIONS)
+ break;
+ }
+
+ g_list_free (list);
+
+ return guesses;
+}
+
+static gchar *
+wksc_get_autocorrect_suggestions (WebKitSpellChecker *webkit_checker,
+ const gchar *word)
+{
+ /* Not supported/needed */
+ return NULL;
+}
+
+static void
+spell_checker_learn_word (WebKitSpellChecker *webkit_checker,
+ const gchar *word)
+{
+ /* Carefully, this will add the word to all active dictionaries! */
+
+ ESpellChecker *checker;
+ GHashTable *active_dictionaries;
+ GList *list, *link;
+
+ checker = E_SPELL_CHECKER (webkit_checker);
+ active_dictionaries = checker->priv->active_dictionaries;
+ list = g_hash_table_get_keys (active_dictionaries);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
+
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ e_spell_dictionary_learn_word (dictionary, word, -1);
+ }
+
+ g_list_free (list);
+}
+
+static void
+spell_checker_ignore_word (WebKitSpellChecker *webkit_checker,
+ const gchar *word)
+{
+ /* Carefully, this will add the word to all active dictionaries */
+
+ ESpellChecker *checker;
+ GHashTable *active_dictionaries;
+ GList *list, *link;
+
+ checker = E_SPELL_CHECKER (webkit_checker);
+ active_dictionaries = checker->priv->active_dictionaries;
+ list = g_hash_table_get_keys (active_dictionaries);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
+
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ e_spell_dictionary_ignore_word (dictionary, word, -1);
+ }
+
+ g_list_free (list);
+}
+
+static void
+wksc_update_languages (WebKitSpellChecker *webkit_checker,
+ const gchar *languages)
+{
+ ESpellChecker *checker;
+ GHashTable *active_dictionaries;
+ GQueue queue = G_QUEUE_INIT;
+
+ checker = E_SPELL_CHECKER (webkit_checker);
+ active_dictionaries = checker->priv->active_dictionaries;
+
+ if (languages != NULL) {
+ gchar **langs;
+ gint ii;
+
+ langs = g_strsplit (languages, ",", -1);
+ for (ii = 0; langs[ii] != NULL; ii++) {
+ ESpellDictionary *dictionary;
+
+ dictionary = e_spell_checker_ref_dictionary (
+ checker, langs[ii]);
+ if (dictionary != NULL)
+ g_queue_push_tail (&queue, dictionary);
+ }
+ g_strfreev (langs);
+ } else {
+ ESpellDictionary *dictionary;
+ PangoLanguage *pango_language;
+ const gchar *language;
+
+ pango_language = gtk_get_default_language ();
+ language = pango_language_to_string (pango_language);
+ dictionary = e_spell_checker_ref_dictionary (checker, language);
+
+ if (dictionary == NULL) {
+ GList *list;
+
+ list = e_spell_checker_list_available_dicts (checker);
+ if (list != NULL) {
+ dictionary = g_object_ref (list->data);
+ g_list_free (list);
+ }
+ }
+
+ if (dictionary != NULL)
+ g_queue_push_tail (&queue, dictionary);
+ }
+
+ g_hash_table_remove_all (active_dictionaries);
+
+ while (!g_queue_is_empty (&queue)) {
+ ESpellDictionary *dictionary;
+
+ dictionary = g_queue_pop_head (&queue);
+ g_hash_table_add (active_dictionaries, dictionary);
+ }
+
+ g_object_notify (G_OBJECT (checker), "active-languages");
+}
+
+static void
+spell_checker_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_LANGUAGES:
+ g_value_take_boxed (
+ value,
+ e_spell_checker_list_active_languages (
+ E_SPELL_CHECKER (object), NULL));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_checker_dispose (GObject *object)
+{
+ ESpellCheckerPrivate *priv;
+
+ priv = E_SPELL_CHECKER_GET_PRIVATE (object);
+
+ g_hash_table_remove_all (priv->active_dictionaries);
+ g_hash_table_remove_all (priv->dictionaries_cache);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_spell_checker_parent_class)->dispose (object);
+}
+
+static void
+spell_checker_finalize (GObject *object)
+{
+ ESpellCheckerPrivate *priv;
+
+ priv = E_SPELL_CHECKER_GET_PRIVATE (object);
+
+ /* Freeing EnchantDicts requires help from EnchantBroker. */
+ g_hash_table_foreach_remove (
+ priv->enchant_dicts,
+ spell_checker_enchant_dicts_foreach_cb,
+ priv->broker);
+ g_hash_table_destroy (priv->enchant_dicts);
+
+ enchant_broker_free (priv->broker);
+
+ g_hash_table_destroy (priv->active_dictionaries);
+ g_hash_table_destroy (priv->dictionaries_cache);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_spell_checker_parent_class)->finalize (object);
+}
+
+static void
+spell_checker_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_spell_checker_parent_class)->constructed (object);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+e_spell_checker_class_init (ESpellCheckerClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ESpellCheckerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = spell_checker_get_property;
+ object_class->dispose = spell_checker_dispose;
+ object_class->finalize = spell_checker_finalize;
+ object_class->constructed = spell_checker_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVE_LANGUAGES,
+ g_param_spec_boxed (
+ "active-languages",
+ "Active Languages",
+ "Active spell check language codes",
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *interface)
+{
+ interface->check_spelling_of_string = wksc_check_spelling;
+ interface->get_autocorrect_suggestions_for_misspelled_word =
+ wksc_get_autocorrect_suggestions;
+ interface->get_guesses_for_word = wksc_get_guesses;
+ interface->ignore_word = spell_checker_ignore_word;
+ interface->learn_word = spell_checker_learn_word;
+ interface->update_spell_checking_languages = wksc_update_languages;
+}
+
+static void
+e_spell_checker_init (ESpellChecker *checker)
+{
+ GHashTable *active_dictionaries;
+ GHashTable *dictionaries_cache;
+ GHashTable *enchant_dicts;
+
+ active_dictionaries = g_hash_table_new_full (
+ (GHashFunc) e_spell_dictionary_hash,
+ (GEqualFunc) e_spell_dictionary_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) NULL);
+
+ dictionaries_cache = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) g_object_unref);
+
+ enchant_dicts = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) NULL);
+
+ checker->priv = E_SPELL_CHECKER_GET_PRIVATE (checker);
+
+ checker->priv->broker = enchant_broker_init ();
+ checker->priv->active_dictionaries = active_dictionaries;
+ checker->priv->dictionaries_cache = dictionaries_cache;
+ checker->priv->enchant_dicts = enchant_dicts;
+}
+
+/**
+ * e_spell_checker_new:
+ *
+ * Creates a new #ESpellChecker instance.
+ *
+ * Returns: a new #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_checker_new (void)
+{
+ return g_object_new (E_TYPE_SPELL_CHECKER, NULL);
+}
+
+static void
+list_enchant_dicts (const gchar * const lang_tag,
+ const gchar * const provider_name,
+ const gchar * const provider_desc,
+ const gchar * const provider_file,
+ gpointer user_data)
+{
+ ESpellChecker *checker = user_data;
+ EnchantDict *enchant_dict;
+
+ enchant_dict = enchant_broker_request_dict (
+ checker->priv->broker, lang_tag);
+ if (enchant_dict != NULL) {
+ ESpellDictionary *dictionary;
+ const gchar *code;
+
+ /* Note that we retain ownership of the EnchantDict.
+ * Since EnchantDict is not reference counted, we're
+ * merely loaning the pointer to ESpellDictionary. */
+ dictionary = e_spell_dictionary_new (checker, enchant_dict);
+ code = e_spell_dictionary_get_code (dictionary);
+
+ g_hash_table_insert (
+ checker->priv->dictionaries_cache,
+ (gpointer) code, dictionary);
+
+ g_hash_table_insert (
+ checker->priv->enchant_dicts,
+ g_strdup (code), enchant_dict);
+ }
+}
+
+/**
+ * e_spell_checker_list_available_dicts:
+ * @checker: An #ESpellChecker
+ *
+ * Returns list of all dictionaries available to the actual
+ * spell-checking backend.
+ *
+ * Returns: new copy of #GList of #ESpellDictionary. The dictionaries are
+ * owned by the @checker and should not be free'd. The list should be freed
+ * using g_list_free() when not neede anymore. [transfer-list]
+ */
+GList *
+e_spell_checker_list_available_dicts (ESpellChecker *checker)
+{
+ GList *list;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+ if (!checker->priv->dictionaries_loaded) {
+ enchant_broker_list_dicts (
+ checker->priv->broker,
+ list_enchant_dicts, checker);
+ checker->priv->dictionaries_loaded = TRUE;
+ }
+
+ list = g_hash_table_get_values (checker->priv->dictionaries_cache);
+
+ return g_list_sort (list, (GCompareFunc) e_spell_dictionary_compare);
+}
+
+/**
+ * e_spell_checker_ref_dictionary:
+ * @checker: an #ESpellChecker
+ * @language_code: (allow-none): language code of a dictionary, or %NULL
+ *
+ * Tries to find an #ESpellDictionary for given @language_code.
+ * If @language_code is %NULL, the function will return a default
+ * #ESpellDictionary.
+ *
+ * Returns: an #ESpellDictionary for @language_code
+ */
+ESpellDictionary *
+e_spell_checker_ref_dictionary (ESpellChecker *checker,
+ const gchar *language_code)
+{
+ ESpellDictionary *dictionary;
+ GList *list;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+ /* If the cache has not yet been initialized, do so - we will need
+ * it anyway, Otherwise is this call very cheap */
+ list = e_spell_checker_list_available_dicts (checker);
+
+ if (language_code == NULL) {
+ dictionary = (list != NULL) ? list->data : NULL;
+ } else {
+ dictionary = g_hash_table_lookup (
+ checker->priv->dictionaries_cache,
+ language_code);
+ }
+
+ if (dictionary != NULL)
+ g_object_ref (dictionary);
+
+ g_list_free (list);
+
+ return dictionary;
+}
+
+/**
+ * e_spell_checker_get_enchant_dict:
+ * @checker: an #ESpellChecker
+ * @language_code: language code of a dictionary, or %NULL
+ *
+ * Returns the #EnchantDict for @language_code, or %NULL if there is none.
+ *
+ * Returns: the #EnchantDict for @language_code, or %NULL
+ **/
+EnchantDict *
+e_spell_checker_get_enchant_dict (ESpellChecker *checker,
+ const gchar *language_code)
+{
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+ g_return_val_if_fail (language_code != NULL, NULL);
+
+ return g_hash_table_lookup (
+ checker->priv->enchant_dicts, language_code);
+}
+
+gboolean
+e_spell_checker_get_language_active (ESpellChecker *checker,
+ const gchar *language_code)
+{
+ ESpellDictionary *dictionary;
+ GHashTable *active_dictionaries;
+ gboolean active;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), FALSE);
+ g_return_val_if_fail (language_code != NULL, FALSE);
+
+ dictionary = e_spell_checker_ref_dictionary (checker, language_code);
+ g_return_val_if_fail (dictionary != NULL, FALSE);
+
+ active_dictionaries = checker->priv->active_dictionaries;
+ active = g_hash_table_contains (active_dictionaries, dictionary);
+
+ g_object_unref (dictionary);
+
+ return active;
+}
+
+void
+e_spell_checker_set_language_active (ESpellChecker *checker,
+ const gchar *language_code,
+ gboolean active)
+{
+ ESpellDictionary *dictionary;
+ GHashTable *active_dictionaries;
+ gboolean is_active;
+
+ g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+ g_return_if_fail (language_code != NULL);
+
+ dictionary = e_spell_checker_ref_dictionary (checker, language_code);
+ g_return_if_fail (dictionary != NULL);
+
+ active_dictionaries = checker->priv->active_dictionaries;
+ is_active = g_hash_table_contains (active_dictionaries, dictionary);
+
+ if (active && !is_active) {
+ g_object_ref (dictionary);
+ g_hash_table_add (active_dictionaries, dictionary);
+ g_object_notify (G_OBJECT (checker), "active-languages");
+ } else if (!active && is_active) {
+ g_hash_table_remove (active_dictionaries, dictionary);
+ g_object_notify (G_OBJECT (checker), "active-languages");
+ }
+
+ g_object_unref (dictionary);
+}
+
+/**
+ * e_spell_checker_list_active_languages:
+ * @checker: an #ESpellChecker
+ * @n_languages: return location for the number of active languages, or %NULL
+ *
+ * Returns a %NULL-terminated array of language codes actively being used
+ * for spell checking. Free the returned array with g_strfreev().
+ *
+ * Returns: a %NULL-teriminated array of language codes
+ **/
+gchar **
+e_spell_checker_list_active_languages (ESpellChecker *checker,
+ guint *n_languages)
+{
+ GHashTable *active_dictionaries;
+ GList *list, *link;
+ gchar **active_languages;
+ guint size;
+ gint ii = 0;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+ active_dictionaries = checker->priv->active_dictionaries;
+ list = g_hash_table_get_keys (active_dictionaries);
+ size = g_hash_table_size (active_dictionaries);
+
+ active_languages = g_new0 (gchar *, size + 1);
+
+ list = g_list_sort (list, (GCompareFunc) e_spell_dictionary_compare);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
+ const gchar *language_code;
+
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ language_code = e_spell_dictionary_get_code (dictionary);
+ active_languages[ii++] = g_strdup (language_code);
+ }
+
+ if (n_languages != NULL)
+ *n_languages = size;
+
+ g_list_free (list);
+
+ return active_languages;
+}
+
+/**
+ * e_spell_checker_count_active_languages:
+ * @checker: an #ESpellChecker
+ *
+ * Returns the number of languages actively being used for spell checking.
+ *
+ * Returns: number of active spell checking languages
+ **/
+guint
+e_spell_checker_count_active_languages (ESpellChecker *checker)
+{
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), 0);
+
+ return g_hash_table_size (checker->priv->active_dictionaries);
+}
+
+/**
+ * e_spell_checker_check_word:
+ * @checker: an #SpellChecker
+ * @word: a word to spell-check
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Calls e_spell_dictionary_check_word() on all active dictionaries in
+ * @checker, and returns %TRUE if @word is recognized by any of them.
+ *
+ * Returns: %TRUE if @word is recognized, %FALSE otherwise
+ **/
+gboolean
+e_spell_checker_check_word (ESpellChecker *checker,
+ const gchar *word,
+ gsize length)
+{
+ GList *list, *link;
+ gboolean recognized = FALSE;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), TRUE);
+ g_return_val_if_fail (word != NULL && *word != '\0', TRUE);
+
+ list = g_hash_table_get_keys (checker->priv->active_dictionaries);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
+
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ if (e_spell_dictionary_check_word (dictionary, word, length)) {
+ recognized = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (list);
+
+ return recognized;
+}
+
+/**
+ * e_spell_checker_ignore_word:
+ * @checker: an #ESpellChecker
+ * @word: word to ignore for the rest of session
+ *
+ * Calls e_spell_dictionary_ignore_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_ignore_word (ESpellChecker *checker,
+ const gchar *word)
+{
+ WebKitSpellCheckerInterface *interface;
+
+ g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+ interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+ interface->ignore_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
+
+/**
+ * e_spell_checker_learn_word:
+ * @checker: an #ESpellChecker
+ * @word: word to learn
+ *
+ * Calls e_spell_dictionary_learn_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_learn_word (ESpellChecker *checker,
+ const gchar *word)
+{
+ WebKitSpellCheckerInterface *interface;
+
+ g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+ interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+ interface->learn_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h
new file mode 100644
index 0000000000..48303d66a9
--- /dev/null
+++ b/e-util/e-spell-checker.h
@@ -0,0 +1,95 @@
+/*
+ * e-spell-checker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_CHECKER_H
+#define E_SPELL_CHECKER_H
+
+#include <glib-object.h>
+#include <e-util/e-spell-dictionary.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_CHECKER \
+ (e_spell_checker_get_type ())
+#define E_SPELL_CHECKER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SPELL_CHECKER, ESpellChecker))
+#define E_SPELL_CHECKER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+#define E_IS_SPELL_CHECKER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SPELL_CHECKER))
+#define E_IS_SPELL_CHECKER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SPELL_CHECKER))
+#define E_SPELL_CHECKER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellChecker ESpellChecker;
+typedef struct _ESpellCheckerPrivate ESpellCheckerPrivate;
+typedef struct _ESpellCheckerClass ESpellCheckerClass;
+
+struct _ESpellChecker {
+ GObject parent;
+ ESpellCheckerPrivate *priv;
+};
+
+struct _ESpellCheckerClass {
+ GObjectClass parent_class;
+};
+
+GType e_spell_checker_get_type (void) G_GNUC_CONST;
+ESpellChecker * e_spell_checker_new (void);
+GList * e_spell_checker_list_available_dicts
+ (ESpellChecker *checker);
+ESpellDictionary *
+ e_spell_checker_ref_dictionary (ESpellChecker *checker,
+ const gchar *language_code);
+EnchantDict * e_spell_checker_get_enchant_dict
+ (ESpellChecker *checker,
+ const gchar *language_code);
+gboolean e_spell_checker_get_language_active
+ (ESpellChecker *checker,
+ const gchar *language_code);
+void e_spell_checker_set_language_active
+ (ESpellChecker *checker,
+ const gchar *language_code,
+ gboolean active);
+gchar ** e_spell_checker_list_active_languages
+ (ESpellChecker *checker,
+ guint *n_languages);
+guint e_spell_checker_count_active_languages
+ (ESpellChecker *checker);
+gboolean e_spell_checker_check_word (ESpellChecker *checker,
+ const gchar *word,
+ gsize length);
+void e_spell_checker_learn_word (ESpellChecker *checker,
+ const gchar *word);
+void e_spell_checker_ignore_word (ESpellChecker *checker,
+ const gchar *word);
+
+G_END_DECLS
+
+#endif /* E_SPELL_CHECKER_H */
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
new file mode 100644
index 0000000000..e6e06b7d9a
--- /dev/null
+++ b/e-util/e-spell-dictionary.c
@@ -0,0 +1,797 @@
+/*
+ * e-spell-dictionary.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-dictionary.h"
+#include "e-spell-checker.h"
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#define E_SPELL_DICTIONARY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryPrivate))
+
+/**
+ * ESpellDictionary:
+ *
+ * The #ESpellDictionary is a wrapper around #EnchantDict.
+ */
+
+enum {
+ PROP_0,
+ PROP_SPELL_CHECKER
+};
+
+struct _ESpellDictionaryPrivate {
+ GWeakRef spell_checker;
+
+ gchar *name;
+ gchar *code;
+ gchar *collate_key;
+};
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN "iso_3166"
+
+static GHashTable *iso_639_table = NULL;
+static GHashTable *iso_3166_table = NULL;
+
+G_DEFINE_TYPE (
+ ESpellDictionary,
+ e_spell_dictionary,
+ G_TYPE_OBJECT);
+
+#ifdef HAVE_ISO_CODES
+
+#define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
+
+#ifdef G_OS_WIN32
+#ifdef DATADIR
+#undef DATADIR
+#endif
+#include <shlobj.h>
+static HMODULE hmodule;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ hmodule = hinstDLL;
+ break;
+ }
+
+ return TRUE;
+}
+
+static gchar *
+_get_iso_codes_prefix (void)
+{
+ static gchar retval[1000];
+ static gint beenhere = 0;
+ gchar *temp_dir = 0;
+
+ if (beenhere)
+ return retval;
+
+ if (!(temp_dir = g_win32_get_package_installation_directory_of_module ((gpointer) hmodule))) {
+ strcpy (retval, ISO_CODES_PREFIX);
+ return retval;
+ }
+
+ strcpy (retval, temp_dir);
+ g_free (temp_dir);
+ beenhere = 1;
+ return retval;
+}
+
+static gchar *
+_get_isocodeslocaledir (void)
+{
+ static gchar retval[1000];
+ static gint beenhere = 0;
+
+ if (beenhere)
+ return retval;
+
+ strcpy (retval, _get_iso_codes_prefix ());
+ strcat (retval, "\\share\\locale" );
+ beenhere = 1;
+ return retval;
+}
+
+#undef ISO_CODES_PREFIX
+#define ISO_CODES_PREFIX _get_iso_codes_prefix ()
+
+#undef ISOCODESLOCALEDIR
+#define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
+
+#endif
+
+static void
+iso_639_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer data,
+ GError **error)
+{
+ GHashTable *hash_table = data;
+ const gchar *iso_639_1_code = NULL;
+ const gchar *iso_639_2_code = NULL;
+ const gchar *name = NULL;
+ const gchar *code = NULL;
+ gint ii;
+
+ if (g_strcmp0 (element_name, "iso_639_entry") != 0) {
+ return;
+ }
+
+ for (ii = 0; attribute_names[ii] != NULL; ii++) {
+ if (strcmp (attribute_names[ii], "name") == 0)
+ name = attribute_values[ii];
+ else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
+ iso_639_1_code = attribute_values[ii];
+ else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
+ iso_639_2_code = attribute_values[ii];
+ }
+
+ code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
+
+ if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+ g_hash_table_insert (
+ hash_table, g_strdup (code),
+ g_strdup (dgettext (ISO_639_DOMAIN, name)));
+}
+
+static void
+iso_3166_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer data,
+ GError **error)
+{
+ GHashTable *hash_table = data;
+ const gchar *name = NULL;
+ const gchar *code = NULL;
+ gint ii;
+
+ if (strcmp (element_name, "iso_3166_entry") != 0)
+ return;
+
+ for (ii = 0; attribute_names[ii] != NULL; ii++) {
+ if (strcmp (attribute_names[ii], "name") == 0)
+ name = attribute_values[ii];
+ else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
+ code = attribute_values[ii];
+ }
+
+ if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+ g_hash_table_insert (
+ hash_table, g_ascii_strdown (code, -1),
+ g_strdup (dgettext (ISO_3166_DOMAIN, name)));
+}
+
+static GMarkupParser iso_639_parser = {
+ iso_639_start_element,
+ NULL, NULL, NULL, NULL
+};
+
+static GMarkupParser iso_3166_parser = {
+ iso_3166_start_element,
+ NULL, NULL, NULL, NULL
+};
+
+static void
+iso_codes_parse (const GMarkupParser *parser,
+ const gchar *basename,
+ GHashTable *hash_table)
+{
+ GMappedFile *mapped_file;
+ gchar *filename;
+ GError *error = NULL;
+
+ filename = g_build_filename (
+ ISO_CODES_PREFIX, "share", "xml",
+ "iso-codes", basename, NULL);
+ mapped_file = g_mapped_file_new (filename, FALSE, &error);
+ g_free (filename);
+
+ if (mapped_file != NULL) {
+ GMarkupParseContext *context;
+ const gchar *contents;
+ gsize length;
+
+ context = g_markup_parse_context_new (
+ parser, 0, hash_table, NULL);
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+ g_markup_parse_context_parse (
+ context, contents, length, &error);
+ g_markup_parse_context_free (context);
+#if GLIB_CHECK_VERSION(2,21,3)
+ g_mapped_file_unref (mapped_file);
+#else
+ g_mapped_file_free (mapped_file);
+#endif
+ }
+
+ if (error != NULL) {
+ g_warning ("%s: %s", basename, error->message);
+ g_error_free (error);
+ }
+}
+
+#endif /* HAVE_ISO_CODES */
+
+struct _enchant_dict_description_data {
+ gchar *language_tag;
+ gchar *dict_name;
+};
+
+static void
+describe_dictionary (const gchar *language_tag,
+ const gchar *provider_name,
+ const gchar *provider_desc,
+ const gchar *provider_file,
+ gpointer user_data)
+{
+ struct _enchant_dict_description_data *data = user_data;
+ const gchar *iso_639_name;
+ const gchar *iso_3166_name;
+ gchar *language_name;
+ gchar *lowercase;
+ gchar **tokens;
+
+ /* Split language code into lowercase tokens. */
+ lowercase = g_ascii_strdown (language_tag, -1);
+ tokens = g_strsplit (lowercase, "_", -1);
+ g_free (lowercase);
+
+ g_return_if_fail (tokens != NULL);
+
+ iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
+
+ if (iso_639_name == NULL) {
+ language_name = g_strdup_printf (
+ /* Translators: %s is the language ISO code. */
+ C_("language", "Unknown (%s)"), language_tag);
+ goto exit;
+ }
+
+ if (g_strv_length (tokens) < 2) {
+ language_name = g_strdup (iso_639_name);
+ goto exit;
+ }
+
+ iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
+
+ if (iso_3166_name != NULL)
+ language_name = g_strdup_printf (
+ /* Translators: The first %s is the language name, and the
+ * second is the country name. Example: "French (France)" */
+ C_("language", "%s (%s)"), iso_639_name, iso_3166_name);
+ else
+ language_name = g_strdup_printf (
+ /* Translators: The first %s is the language name, and the
+ * second is the country name. Example: "French (France)" */
+ C_("language", "%s (%s)"), iso_639_name, tokens[1]);
+
+exit:
+ g_strfreev (tokens);
+
+ data->language_tag = g_strdup (language_tag);
+ data->dict_name = language_name;
+}
+
+static void
+spell_dictionary_set_enchant_dict (ESpellDictionary *dictionary,
+ EnchantDict *enchant_dict)
+{
+ struct _enchant_dict_description_data data;
+
+ enchant_dict_describe (enchant_dict, describe_dictionary, &data);
+
+ dictionary->priv->code = data.language_tag;
+ dictionary->priv->name = data.dict_name;
+ dictionary->priv->collate_key = g_utf8_collate_key (data.dict_name, -1);
+}
+
+static void
+spell_dictionary_set_spell_checker (ESpellDictionary *dictionary,
+ ESpellChecker *spell_checker)
+{
+ g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
+
+ g_weak_ref_set (&dictionary->priv->spell_checker, spell_checker);
+}
+
+static void
+spell_dictionary_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SPELL_CHECKER:
+ spell_dictionary_set_spell_checker (
+ E_SPELL_DICTIONARY (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SPELL_CHECKER:
+ g_value_take_object (
+ value,
+ e_spell_dictionary_ref_spell_checker (
+ E_SPELL_DICTIONARY (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_dispose (GObject *object)
+{
+ ESpellDictionaryPrivate *priv;
+
+ priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
+
+ g_weak_ref_set (&priv->spell_checker, NULL);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_spell_dictionary_parent_class)->dispose (object);
+}
+
+static void
+spell_dictionary_finalize (GObject *object)
+{
+ ESpellDictionaryPrivate *priv;
+
+ priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
+
+ g_free (priv->name);
+ g_free (priv->code);
+ g_free (priv->collate_key);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_spell_dictionary_parent_class)->finalize (object);
+}
+
+static void
+e_spell_dictionary_class_init (ESpellDictionaryClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ESpellDictionaryPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = spell_dictionary_set_property;
+ object_class->get_property = spell_dictionary_get_property;
+ object_class->dispose = spell_dictionary_dispose;
+ object_class->finalize = spell_dictionary_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SPELL_CHECKER,
+ g_param_spec_object (
+ "spell-checker",
+ NULL,
+ "Parent spell checker",
+ E_TYPE_SPELL_CHECKER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_spell_dictionary_init (ESpellDictionary *dictionary)
+{
+ dictionary->priv = E_SPELL_DICTIONARY_GET_PRIVATE (dictionary);
+
+ if (!iso_639_table && !iso_3166_table) {
+#if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
+ bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
+ bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
+
+ bindtextdomain (ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
+ bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
+#endif /* ENABLE_NLS && HAVE_ISO_CODES */
+
+ iso_639_table = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ iso_3166_table = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+#ifdef HAVE_ISO_CODES
+ iso_codes_parse (
+ &iso_639_parser, "iso_639.xml", iso_639_table);
+ iso_codes_parse (
+ &iso_3166_parser, "iso_3166.xml", iso_3166_table);
+#endif /* HAVE_ISO_CODES */
+ }
+}
+
+ESpellDictionary *
+e_spell_dictionary_new (ESpellChecker *spell_checker,
+ EnchantDict *enchant_dict)
+{
+ ESpellDictionary *dictionary;
+
+ g_return_val_if_fail (E_IS_SPELL_CHECKER (spell_checker), NULL);
+ g_return_val_if_fail (enchant_dict != NULL, NULL);
+
+ dictionary = g_object_new (
+ E_TYPE_SPELL_DICTIONARY,
+ "spell-checker", spell_checker, NULL);
+
+ /* Since EnchantDict is not reference counted, ESpellChecker
+ * is loaning us the EnchantDict pointer. We do not own it. */
+ spell_dictionary_set_enchant_dict (dictionary, enchant_dict);
+
+ return dictionary;
+}
+
+/**
+ * e_spell_dictionary_hash:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Generates a hash value for @dictionary based on its ISO code.
+ * This function is intended for easily hashing an #ESpellDictionary
+ * to add to a #GHashTable or similar data structure.
+ *
+ * Returns: a hash value for @dictionary
+ **/
+guint
+e_spell_dictionary_hash (ESpellDictionary *dictionary)
+{
+ const gchar *code;
+
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), 0);
+
+ code = e_spell_dictionary_get_code (dictionary);
+
+ return g_str_hash (code);
+}
+
+/**
+ * e_spell_dictionary_equal:
+ * @dictionary1: an #ESpellDictionary
+ * @dictionary2: another #ESpellDictionary
+ *
+ * Checks two #ESpellDictionary instances for equality based on their
+ * ISO codes.
+ *
+ * Returns: %TRUE if @dictionary1 and @dictionary2 are equal
+ **/
+gboolean
+e_spell_dictionary_equal (ESpellDictionary *dictionary1,
+ ESpellDictionary *dictionary2)
+{
+ const gchar *code1, *code2;
+
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1), FALSE);
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2), FALSE);
+
+ if (dictionary1 == dictionary2)
+ return TRUE;
+
+ code1 = e_spell_dictionary_get_code (dictionary1);
+ code2 = e_spell_dictionary_get_code (dictionary2);
+
+ return g_str_equal (code1, code2);
+}
+
+/**
+ * e_spell_dictionary_compare:
+ * @dictionary1: an #ESpellDictionary
+ * @dictionary2: another #ESpellDictionary
+ *
+ * Compares @dictionary1 and @dictionary2 by their display names for
+ * the purpose of lexicographical sorting. Use this function where a
+ * #GCompareFunc callback is required, such as g_list_sort().
+ *
+ * Returns: 0 if the names match,
+ * a negative value if @dictionary1 < @dictionary2,
+ * or a positive value of @dictionary1 > @dictionary2
+ **/
+gint
+e_spell_dictionary_compare (ESpellDictionary *dictionary1,
+ ESpellDictionary *dictionary2)
+{
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1), 0);
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2), 0);
+
+ return strcmp (
+ dictionary1->priv->collate_key,
+ dictionary2->priv->collate_key);
+}
+
+/**
+ * e_spell_dictionary_get_name:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns the display name of the dictionary (for example
+ * "English (British)")
+ *
+ * Returns: the display name of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_name (ESpellDictionary *dictionary)
+{
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+ return dictionary->priv->name;
+}
+
+/**
+ * e_spell_dictionary_get_code:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns the ISO code of the spell-checking language for
+ * @dictionary (for example "en_US").
+ *
+ * Returns: the language code of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_code (ESpellDictionary *dictionary)
+{
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+ return dictionary->priv->code;
+}
+
+/**
+ * e_spell_dictionary_ref_spell_checker:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns a new reference to the #ESpellChecker which owns the dictionary.
+ * Unreference the #ESpellChecker with g_object_unref() when finished with it.
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_dictionary_ref_spell_checker (ESpellDictionary *dictionary)
+{
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+ return g_weak_ref_get (&dictionary->priv->spell_checker);
+}
+
+/**
+ * e_spell_dictionary_check_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to spell-check
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Tries to lookup the @word in the @dictionary to check whether
+ * it's spelled correctly or not.
+ *
+ * Returns: %TRUE if @word is recognized, %FALSE otherwise
+ */
+gboolean
+e_spell_dictionary_check_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length)
+{
+ ESpellChecker *spell_checker;
+ EnchantDict *enchant_dict;
+ gboolean recognized;
+
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), TRUE);
+ g_return_val_if_fail (word != NULL && *word != '\0', TRUE);
+
+ spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+ g_return_val_if_fail (spell_checker != NULL, TRUE);
+
+ enchant_dict = e_spell_checker_get_enchant_dict (
+ spell_checker, e_spell_dictionary_get_code (dictionary));
+ g_return_val_if_fail (enchant_dict != NULL, TRUE);
+
+ recognized = (enchant_dict_check (enchant_dict, word, length) == 0);
+
+ g_object_unref (spell_checker);
+
+ return recognized;
+}
+
+/**
+ * e_spell_dictionary_learn_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to @dictionary
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Permanently adds @word to @dictionary so that next time calling
+ * e_spell_dictionary_check() on the @word will return %TRUE.
+ */
+void
+e_spell_dictionary_learn_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length)
+{
+ ESpellChecker *spell_checker;
+ EnchantDict *enchant_dict;
+
+ g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+ g_return_if_fail (word != NULL && *word != '\0');
+
+ spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+ g_return_if_fail (spell_checker != NULL);
+
+ enchant_dict = e_spell_checker_get_enchant_dict (
+ spell_checker, e_spell_dictionary_get_code (dictionary));
+ g_return_if_fail (enchant_dict != NULL);
+
+ enchant_dict_add_to_personal (enchant_dict, word, length);
+
+ g_object_unref (spell_checker);
+}
+
+/**
+ * e_spell_dictionary_ignore_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to ignore list
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Adds @word to temporary ignore list of the @dictionary, so that
+ * e_spell_dictionary_check() on the @word will return %TRUE. The
+ * list is cleared when the dictionary is freed.
+ */
+void
+e_spell_dictionary_ignore_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length)
+{
+ ESpellChecker *spell_checker;
+ EnchantDict *enchant_dict;
+
+ g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+ g_return_if_fail (word != NULL && *word != '\0');
+
+ spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+ g_return_if_fail (spell_checker != NULL);
+
+ enchant_dict = e_spell_checker_get_enchant_dict (
+ spell_checker, e_spell_dictionary_get_code (dictionary));
+ g_return_if_fail (enchant_dict != NULL);
+
+ enchant_dict_add_to_session (enchant_dict, word, length);
+
+ g_object_unref (spell_checker);
+}
+
+/**
+ * e_spell_dictionary_get_suggestions:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to which to find suggestions
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Provides list of alternative spellings of @word.
+ *
+ * Free the returned spelling suggestions with g_free(), and the list
+ * itself with g_list_free(). An easy way to free the list properly in
+ * one step is as follows:
+ *
+ * |[
+ * g_list_free_full (list, (GDestroyNotify) g_free);
+ * ]|
+ *
+ * Returns: a list of spelling suggestions for @word
+ */
+GList *
+e_spell_dictionary_get_suggestions (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length)
+{
+ ESpellChecker *spell_checker;
+ EnchantDict *enchant_dict;
+ GList *list = NULL;
+ gchar **suggestions;
+ gsize ii, count = 0;
+
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+ g_return_val_if_fail (word != NULL && *word != '\0', NULL);
+
+ spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+ g_return_val_if_fail (spell_checker != NULL, NULL);
+
+ enchant_dict = e_spell_checker_get_enchant_dict (
+ spell_checker, e_spell_dictionary_get_code (dictionary));
+ g_return_val_if_fail (enchant_dict != NULL, NULL);
+
+ suggestions = enchant_dict_suggest (enchant_dict, word, length, &count);
+ for (ii = 0; ii < count; ii++)
+ list = g_list_prepend (list, g_strdup (suggestions[ii]));
+ enchant_dict_free_suggestions (enchant_dict, suggestions);
+
+ g_object_unref (spell_checker);
+
+ return g_list_reverse (list);
+}
+
+/**
+ * e_spell_dictionary_add_correction
+ * @dictionary: an #ESpellDictionary
+ * @misspelled: a misspelled word
+ * @misspelled_length: length of @misspelled in bytes or -1 when
+ * %NULL-terminated
+ * @correction: the corrected word
+ * @correction_length: length of @correction in bytes or -1 when
+ * %NULL-terminated
+ *
+ * Learns a new @correction of @misspelled word.
+ */
+void
+e_spell_dictionary_store_correction (ESpellDictionary *dictionary,
+ const gchar *misspelled,
+ gsize misspelled_length,
+ const gchar *correction,
+ gsize correction_length)
+{
+ ESpellChecker *spell_checker;
+ EnchantDict *enchant_dict;
+
+ g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+ g_return_if_fail (misspelled != NULL && *misspelled != '\0');
+ g_return_if_fail (correction != NULL && *correction != '\0');
+
+ spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+ g_return_if_fail (spell_checker != NULL);
+
+ enchant_dict = e_spell_checker_get_enchant_dict (
+ spell_checker, e_spell_dictionary_get_code (dictionary));
+ g_return_if_fail (enchant_dict != NULL);
+
+ enchant_dict_store_replacement (
+ enchant_dict,
+ misspelled, misspelled_length,
+ correction, correction_length);
+
+ g_object_unref (spell_checker);
+}
+
diff --git a/e-util/e-spell-dictionary.h b/e-util/e-spell-dictionary.h
new file mode 100644
index 0000000000..f36bfb4704
--- /dev/null
+++ b/e-util/e-spell-dictionary.h
@@ -0,0 +1,99 @@
+/*
+ * e-spell-dictionary.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_DICTIONARY_H
+#define E_SPELL_DICTIONARY_H
+
+#include <glib-object.h>
+#include <enchant/enchant.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_DICTIONARY \
+ (e_spell_dictionary_get_type ())
+#define E_SPELL_DICTIONARY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionary))
+#define E_SPELL_DICTIONARY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+#define E_IS_SPELL_DICTIONARY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SPELL_DICTIONARY))
+#define E_IS_SPELL_DICTIONARY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SPELL_DICTIONARY))
+#define E_SPELL_DICTIONARY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellDictionary ESpellDictionary;
+typedef struct _ESpellDictionaryPrivate ESpellDictionaryPrivate;
+typedef struct _ESpellDictionaryClass ESpellDictionaryClass;
+typedef struct _ESpellChecker ESpellChecker;
+
+struct _ESpellDictionary {
+ GObject parent;
+ ESpellDictionaryPrivate *priv;
+};
+
+struct _ESpellDictionaryClass {
+ GObjectClass parent_class;
+};
+
+GType e_spell_dictionary_get_type (void) G_GNUC_CONST;
+ESpellDictionary *
+ e_spell_dictionary_new (ESpellChecker *spell_checker,
+ EnchantDict *enchant_dict);
+guint e_spell_dictionary_hash (ESpellDictionary *dictionary);
+gboolean e_spell_dictionary_equal (ESpellDictionary *dictionary1,
+ ESpellDictionary *dictionary2);
+gint e_spell_dictionary_compare (ESpellDictionary *dictionary1,
+ ESpellDictionary *dictionary2);
+const gchar * e_spell_dictionary_get_name (ESpellDictionary *dictionary);
+const gchar * e_spell_dictionary_get_code (ESpellDictionary *dictionary);
+ESpellChecker * e_spell_dictionary_ref_spell_checker
+ (ESpellDictionary *dictionary);
+gboolean e_spell_dictionary_check_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length);
+void e_spell_dictionary_learn_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length);
+void e_spell_dictionary_ignore_word (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length);
+GList * e_spell_dictionary_get_suggestions
+ (ESpellDictionary *dictionary,
+ const gchar *word,
+ gsize length);
+void e_spell_dictionary_store_correction
+ (ESpellDictionary *dictionary,
+ const gchar *misspelled,
+ gsize misspelled_length,
+ const gchar *correction,
+ gsize correction_length);
+
+G_END_DECLS
+
+#endif /* E_SPELL_DICTIONARY_H */
diff --git a/e-util/e-spell-entry.c b/e-util/e-spell-entry.c
index 75c7a6a9c8..4993612a7a 100644
--- a/e-util/e-spell-entry.c
+++ b/e-util/e-spell-entry.c
@@ -23,8 +23,7 @@
#include <libebackend/libebackend.h>
-#include <editor/gtkhtml-spell-language.h>
-#include <editor/gtkhtml-spell-checker.h>
+#include <e-util/e-spell-checker.h>
#include "e-misc-utils.h"
#include "e-spell-entry.h"
@@ -37,18 +36,20 @@ struct _ESpellEntryPrivate {
PangoAttrList *attr_list;
gint mark_character;
gint entry_scroll_offset;
- GSettings *settings;
gboolean custom_checkers;
gboolean checking_enabled;
- GSList *checkers;
gchar **words;
gint *word_starts;
gint *word_ends;
+
+ ESpellChecker *spell_checker;
+ guint active_languages_handler_id;
};
enum {
PROP_0,
- PROP_CHECKING_ENABLED
+ PROP_CHECKING_ENABLED,
+ PROP_SPELL_CHECKER
};
G_DEFINE_TYPE_WITH_CODE (
@@ -76,16 +77,12 @@ word_misspelled (ESpellEntry *entry,
g_strlcpy (word, text + start, end - start + 1);
if (g_unichar_isalpha (*word)) {
- GSList *li;
+ ESpellChecker *spell_checker;
gssize wlen = strlen (word);
- for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
- GtkhtmlSpellChecker *checker = li->data;
- if (gtkhtml_spell_checker_check_word (checker, word, wlen)) {
- result = FALSE;
- break;
- }
- }
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+ if (e_spell_checker_check_word (spell_checker, word, wlen))
+ result = FALSE;
}
g_free (word);
@@ -160,8 +157,13 @@ spell_entry_recheck_all (ESpellEntry *entry)
pango_attr_list_unref (entry->priv->attr_list);
entry->priv->attr_list = pango_attr_list_new ();
- if (e_spell_entry_get_checking_enabled (entry))
- check_words = (entry->priv->checkers != NULL);
+ if (e_spell_entry_get_checking_enabled (entry)) {
+ ESpellChecker *spell_checker;
+
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+ if (e_spell_checker_count_active_languages (spell_checker) > 0)
+ check_words = TRUE;
+ }
if (check_words) {
/* Loop through words */
@@ -269,15 +271,15 @@ add_to_dictionary (GtkWidget *menuitem,
{
gchar *word;
gint start, end;
- GtkhtmlSpellChecker *checker;
+ ESpellDictionary *dict;
get_word_extents_from_position (
entry, &start, &end, entry->priv->mark_character);
word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
- checker = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
- if (checker != NULL)
- gtkhtml_spell_checker_add_word (checker, word, -1);
+ dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
+ if (dict != NULL)
+ e_spell_dictionary_learn_word (dict, word, -1);
g_free (word);
@@ -300,18 +302,16 @@ static void
ignore_all (GtkWidget *menuitem,
ESpellEntry *entry)
{
+ ESpellChecker *spell_checker;
gchar *word;
gint start, end;
- GSList *li;
get_word_extents_from_position (
entry, &start, &end, entry->priv->mark_character);
word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
- for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
- GtkhtmlSpellChecker *checker = li->data;
- gtkhtml_spell_checker_add_word_to_session (checker, word, -1);
- }
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+ e_spell_checker_ignore_word (spell_checker, word);
g_free (word);
@@ -338,7 +338,7 @@ replace_word (GtkWidget *menuitem,
const gchar *newword;
gint start, end;
gint cursor;
- GtkhtmlSpellChecker *checker;
+ ESpellDictionary *dict;
get_word_extents_from_position (
entry, &start, &end, entry->priv->mark_character);
@@ -359,11 +359,11 @@ replace_word (GtkWidget *menuitem,
GTK_EDITABLE (entry), newword, strlen (newword), &start);
gtk_editable_set_position (GTK_EDITABLE (entry), cursor);
- checker = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
+ dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
- if (checker != NULL)
- gtkhtml_spell_checker_store_replacement (
- checker, oldword, -1, newword, -1);
+ if (dict != NULL)
+ e_spell_dictionary_store_correction (
+ dict, oldword, -1, newword, -1);
g_free (oldword);
}
@@ -371,13 +371,13 @@ replace_word (GtkWidget *menuitem,
static void
build_suggestion_menu (ESpellEntry *entry,
GtkWidget *menu,
- GtkhtmlSpellChecker *checker,
+ ESpellDictionary *dict,
const gchar *word)
{
GtkWidget *mi;
GList *suggestions, *iter;
- suggestions = gtkhtml_spell_checker_get_suggestions (checker, word, -1);
+ suggestions = e_spell_dictionary_get_suggestions (dict, word, -1);
if (suggestions == NULL) {
/* no suggestions. Put something in the menu anyway... */
@@ -414,7 +414,7 @@ build_suggestion_menu (ESpellEntry *entry,
}
mi = gtk_menu_item_new_with_label (iter->data);
- g_object_set_data (G_OBJECT (mi), "spell-entry-checker", checker);
+ g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
g_signal_connect (mi, "activate", G_CALLBACK (replace_word), entry);
gtk_widget_show (mi);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
@@ -428,35 +428,49 @@ static GtkWidget *
build_spelling_menu (ESpellEntry *entry,
const gchar *word)
{
- GtkhtmlSpellChecker *checker;
+ ESpellChecker *spell_checker;
+ ESpellDictionary *dict;
GtkWidget *topmenu, *mi;
+ GQueue queue = G_QUEUE_INIT;
+ gchar **active_languages;
+ guint ii, n_active_languages;
gchar *label;
topmenu = gtk_menu_new ();
- if (entry->priv->checkers == NULL)
- return topmenu;
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+
+ active_languages = e_spell_checker_list_active_languages (
+ spell_checker, &n_active_languages);
+ for (ii = 0; ii < n_active_languages; ii++) {
+ dict = e_spell_checker_ref_dictionary (
+ spell_checker, active_languages[ii]);
+ if (dict != NULL)
+ g_queue_push_tail (&queue, dict);
+ }
+ g_strfreev (active_languages);
+
+ if (g_queue_is_empty (&queue))
+ goto exit;
/* Suggestions */
- if (entry->priv->checkers->next == NULL) {
- checker = entry->priv->checkers->data;
- build_suggestion_menu (entry, topmenu, checker, word);
+ if (n_active_languages == 1) {
+ dict = g_queue_peek_head (&queue);
+ build_suggestion_menu (entry, topmenu, dict, word);
} else {
- GSList *li;
GtkWidget *menu;
- const gchar *lang_name;
+ GList *list, *link;
- for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
- const GtkhtmlSpellLanguage *language;
+ list = g_queue_peek_head_link (&queue);
- checker = li->data;
- language = gtkhtml_spell_checker_get_language (checker);
- if (language == NULL)
- continue;
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ const gchar *lang_name;
- lang_name = gtkhtml_spell_language_get_name (language);
+ dict = E_SPELL_DICTIONARY (link->data);
+
+ lang_name = e_spell_dictionary_get_name (dict);
if (lang_name == NULL)
- lang_name = gtkhtml_spell_language_get_code (language);
+ lang_name = e_spell_dictionary_get_code (dict);
if (lang_name == NULL)
lang_name = "???";
@@ -466,7 +480,7 @@ build_spelling_menu (ESpellEntry *entry,
gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
- build_suggestion_menu (entry, menu, checker, word);
+ build_suggestion_menu (entry, menu, dict, word);
}
}
@@ -484,36 +498,34 @@ build_spelling_menu (ESpellEntry *entry,
GTK_IMAGE_MENU_ITEM (mi),
gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_MENU));
- if (entry->priv->checkers->next == NULL) {
- checker = entry->priv->checkers->data;
- g_object_set_data (G_OBJECT (mi), "spell-entry-checker", checker);
+ if (n_active_languages == 1) {
+ dict = g_queue_peek_head (&queue);
+ g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
g_signal_connect (
mi, "activate",
G_CALLBACK (add_to_dictionary), entry);
} else {
- GSList *li;
GtkWidget *menu, *submi;
- const gchar *lang_name;
+ GList *list, *link;
menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
- for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
- const GtkhtmlSpellLanguage *language;
+ list = g_queue_peek_head_link (&queue);
- checker = li->data;
- language = gtkhtml_spell_checker_get_language (checker);
- if (language == NULL)
- continue;
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ const gchar *lang_name;
+
+ dict = E_SPELL_DICTIONARY (link->data);
- lang_name = gtkhtml_spell_language_get_name (language);
+ lang_name = e_spell_dictionary_get_name (dict);
if (lang_name == NULL)
- lang_name = gtkhtml_spell_language_get_code (language);
+ lang_name = e_spell_dictionary_get_code (dict);
if (lang_name == NULL)
lang_name = "???";
submi = gtk_menu_item_new_with_label (lang_name);
- g_object_set_data (G_OBJECT (submi), "spell-entry-checker", checker);
+ g_object_set_data (G_OBJECT (submi), "spell-entry-checker", dict);
g_signal_connect (
submi, "activate",
G_CALLBACK (add_to_dictionary), entry);
@@ -535,6 +547,10 @@ build_spelling_menu (ESpellEntry *entry,
gtk_widget_show_all (mi);
gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
+exit:
+ while (!g_queue_is_empty (&queue))
+ g_object_unref (g_queue_pop_head (&queue));
+
return topmenu;
}
@@ -580,10 +596,12 @@ spell_entry_populate_popup (ESpellEntry *entry,
GtkMenu *menu,
gpointer data)
{
+ ESpellChecker *spell_checker;
gint start, end;
gchar *word;
- if (entry->priv->checkers == NULL)
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+ if (e_spell_checker_count_active_languages (spell_checker) == 0)
return;
get_word_extents_from_position (
@@ -606,8 +624,10 @@ static void
spell_entry_changed (GtkEditable *editable)
{
ESpellEntry *entry = E_SPELL_ENTRY (editable);
+ ESpellChecker *spell_checker;
- if (entry->priv->checkers == NULL)
+ spell_checker = e_spell_entry_get_spell_checker (entry);
+ if (e_spell_checker_count_active_languages (spell_checker) == 0)
return;
if (entry->priv->words != NULL) {
@@ -633,70 +653,6 @@ spell_entry_notify_scroll_offset (ESpellEntry *spell_entry)
&spell_entry->priv->entry_scroll_offset, NULL);
}
-static GList *
-spell_entry_load_spell_languages (void)
-{
- GSettings *settings;
- GList *spell_languages = NULL;
- gchar **strv;
- gint ii;
-
- /* Ask GSettings for a list of spell check language codes. */
- settings = g_settings_new ("org.gnome.evolution.mail");
- strv = g_settings_get_strv (settings, "composer-spell-languages");
- 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;
-
- language = gtkhtml_spell_language_lookup (language_code);
- if (language != NULL)
- spell_languages = g_list_prepend (
- spell_languages, (gpointer) language);
- }
-
- g_strfreev (strv);
-
- spell_languages = g_list_reverse (spell_languages);
-
- /* Pick a default spell language if it came back empty. */
- if (spell_languages == NULL) {
- const GtkhtmlSpellLanguage *language;
-
- language = gtkhtml_spell_language_lookup (NULL);
-
- if (language) {
- spell_languages = g_list_prepend (
- spell_languages, (gpointer) language);
- }
- }
-
- return spell_languages;
-}
-
-static void
-spell_entry_settings_changed (ESpellEntry *spell_entry,
- const gchar *key)
-{
- GList *languages;
-
- g_return_if_fail (spell_entry != NULL);
-
- if (spell_entry->priv->custom_checkers)
- return;
-
- if (key && !g_str_equal (key, "composer-spell-languages"))
- return;
-
- languages = spell_entry_load_spell_languages ();
- e_spell_entry_set_languages (spell_entry, languages);
- g_list_free (languages);
-
- spell_entry->priv->custom_checkers = FALSE;
-}
-
static gint
spell_entry_find_position (ESpellEntry *spell_entry,
gint x)
@@ -722,6 +678,15 @@ spell_entry_find_position (ESpellEntry *spell_entry,
}
static void
+spell_entry_active_languages_cb (ESpellChecker *spell_checker,
+ GParamSpec *pspec,
+ ESpellEntry *spell_entry)
+{
+ if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+ spell_entry_recheck_all (spell_entry);
+}
+
+static void
spell_entry_set_property (GObject *object,
guint property_id,
const GValue *value,
@@ -733,6 +698,12 @@ spell_entry_set_property (GObject *object,
E_SPELL_ENTRY (object),
g_value_get_boolean (value));
return;
+
+ case PROP_SPELL_CHECKER:
+ e_spell_entry_set_spell_checker (
+ E_SPELL_ENTRY (object),
+ g_value_get_object (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -751,6 +722,13 @@ spell_entry_get_property (GObject *object,
e_spell_entry_get_checking_enabled (
E_SPELL_ENTRY (object)));
return;
+
+ case PROP_SPELL_CHECKER:
+ g_value_set_object (
+ value,
+ e_spell_entry_get_spell_checker (
+ E_SPELL_ENTRY (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -763,10 +741,14 @@ spell_entry_dispose (GObject *object)
priv = E_SPELL_ENTRY_GET_PRIVATE (object);
- g_slist_free_full (priv->checkers, (GDestroyNotify) g_object_unref);
- priv->checkers = NULL;
+ if (priv->active_languages_handler_id > 0) {
+ g_signal_handler_disconnect (
+ priv->spell_checker,
+ priv->active_languages_handler_id);
+ priv->active_languages_handler_id = 0;
+ }
- g_clear_object (&priv->settings);
+ g_clear_object (&priv->spell_checker);
if (priv->attr_list != NULL) {
pango_attr_list_unref (priv->attr_list);
@@ -795,9 +777,22 @@ spell_entry_finalize (GObject *object)
static void
spell_entry_constructed (GObject *object)
{
+ ESpellEntry *spell_entry;
+ ESpellChecker *spell_checker;
+
+ spell_entry = E_SPELL_ENTRY (object);
+
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_spell_entry_parent_class)->constructed (object);
+ /* Install a default spell checker if there is not one already. */
+ spell_checker = e_spell_entry_get_spell_checker (spell_entry);
+ if (spell_checker == NULL) {
+ spell_checker = e_spell_checker_new ();
+ e_spell_entry_set_spell_checker (spell_entry, spell_checker);
+ g_object_unref (spell_checker);
+ }
+
e_extensible_load_extensions (E_EXTENSIBLE (object));
}
@@ -860,6 +855,17 @@ e_spell_entry_class_init (ESpellEntryClass *class)
TRUE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SPELL_CHECKER,
+ g_param_spec_object (
+ "spell-checker",
+ "Spell Checker",
+ "The spell checker object",
+ E_TYPE_SPELL_CHECKER,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -867,7 +873,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
{
spell_entry->priv = E_SPELL_ENTRY_GET_PRIVATE (spell_entry);
spell_entry->priv->attr_list = pango_attr_list_new ();
- spell_entry->priv->checkers = NULL;
spell_entry->priv->checking_enabled = TRUE;
g_signal_connect (
@@ -882,15 +887,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
e_signal_connect_notify (
spell_entry, "notify::scroll-offset",
G_CALLBACK (spell_entry_notify_scroll_offset), NULL);
-
- /* listen for languages changes */
- spell_entry->priv->settings = g_settings_new ("org.gnome.evolution.mail");
- g_signal_connect_swapped (
- spell_entry->priv->settings, "changed",
- G_CALLBACK (spell_entry_settings_changed), spell_entry);
-
- /* load current settings */
- spell_entry_settings_changed (spell_entry, NULL);
}
GtkWidget *
@@ -899,36 +895,6 @@ e_spell_entry_new (void)
return g_object_new (E_TYPE_SPELL_ENTRY, NULL);
}
-/* 'languages' consists of 'const GtkhtmlSpellLanguage *' */
-void
-e_spell_entry_set_languages (ESpellEntry *spell_entry,
- GList *languages)
-{
- GList *iter;
-
- g_return_if_fail (spell_entry != NULL);
-
- spell_entry->priv->custom_checkers = TRUE;
-
- if (spell_entry->priv->checkers)
- g_slist_free_full (spell_entry->priv->checkers, g_object_unref);
- spell_entry->priv->checkers = NULL;
-
- for (iter = languages; iter; iter = g_list_next (iter)) {
- const GtkhtmlSpellLanguage *language = iter->data;
-
- if (language)
- spell_entry->priv->checkers = g_slist_prepend (
- spell_entry->priv->checkers,
- gtkhtml_spell_checker_new (language));
- }
-
- spell_entry->priv->checkers = g_slist_reverse (spell_entry->priv->checkers);
-
- if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
- spell_entry_recheck_all (spell_entry);
-}
-
gboolean
e_spell_entry_get_checking_enabled (ESpellEntry *spell_entry)
{
@@ -951,3 +917,66 @@ e_spell_entry_set_checking_enabled (ESpellEntry *spell_entry,
g_object_notify (G_OBJECT (spell_entry), "checking-enabled");
}
+
+/**
+ * e_spell_entry_get_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ *
+ * Returns the #ESpellChecker being used for spell checking. By default,
+ * #ESpellEntry creates its own #ESpellChecker, but this can be overridden
+ * through e_spell_entry_set_spell_checker().
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_entry_get_spell_checker (ESpellEntry *spell_entry)
+{
+ g_return_val_if_fail (E_IS_SPELL_ENTRY (spell_entry), NULL);
+
+ return spell_entry->priv->spell_checker;
+}
+
+/**
+ * e_spell_entry_set_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ * @spell_checker: an #ESpellChecker
+ *
+ * Sets the #ESpellChecker to use for spell checking. By default,
+ * #ESpellEntry creates its own #ESpellChecker. This function can be
+ * useful for sharing an #ESpellChecker across multiple spell-checking
+ * widgets, so the active spell checking languages stay synchronized.
+ **/
+void
+e_spell_entry_set_spell_checker (ESpellEntry *spell_entry,
+ ESpellChecker *spell_checker)
+{
+ gulong handler_id;
+
+ g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
+ g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
+
+ if (spell_checker == spell_entry->priv->spell_checker)
+ return;
+
+ if (spell_entry->priv->spell_checker != NULL) {
+ g_signal_handler_disconnect (
+ spell_entry->priv->spell_checker,
+ spell_entry->priv->active_languages_handler_id);
+ g_object_unref (spell_entry->priv->spell_checker);
+ }
+
+ spell_entry->priv->spell_checker = g_object_ref (spell_checker);
+
+ handler_id = g_signal_connect (
+ spell_checker, "notify::active-languages",
+ G_CALLBACK (spell_entry_active_languages_cb),
+ spell_entry);
+
+ spell_entry->priv->active_languages_handler_id = handler_id;
+
+ g_object_notify (G_OBJECT (spell_entry), "spell-checker");
+
+ if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+ spell_entry_recheck_all (spell_entry);
+}
+
diff --git a/e-util/e-spell-entry.h b/e-util/e-spell-entry.h
index ed23cb0453..a07d68f177 100644
--- a/e-util/e-spell-entry.h
+++ b/e-util/e-spell-entry.h
@@ -24,6 +24,8 @@
#include <gtk/gtk.h>
+#include <e-util/e-util.h>
+
/* Standard GObject macros */
#define E_TYPE_SPELL_ENTRY \
(e_spell_entry_get_type ())
@@ -60,13 +62,14 @@ struct _ESpellEntryClass {
GType e_spell_entry_get_type (void) G_GNUC_CONST;
GtkWidget * e_spell_entry_new (void);
-void e_spell_entry_set_languages (ESpellEntry *spell_entry,
- GList *languages);
gboolean e_spell_entry_get_checking_enabled
(ESpellEntry *spell_entry);
void e_spell_entry_set_checking_enabled
(ESpellEntry *spell_entry,
gboolean enable_checking);
+ESpellChecker * e_spell_entry_get_spell_checker (ESpellEntry *spell_entry);
+void e_spell_entry_set_spell_checker (ESpellEntry *spell_entry,
+ ESpellChecker *spell_checker);
G_END_DECLS
diff --git a/e-util/e-util-enums.h b/e-util/e-util-enums.h
index 9913e4d938..736a901e7a 100644
--- a/e-util/e-util-enums.h
+++ b/e-util/e-util-enums.h
@@ -124,6 +124,227 @@ typedef enum {
E_DURATION_DAYS
} EDurationType;
+typedef enum {
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE = 0,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN,
+ E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA
+} EHTMLEditorSelectionBlockFormat;
+
+/* The values match the actual size in <font size="n"> */
+typedef enum {
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY = 1,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL = 2,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL = 3,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG = 4,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER = 5,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE = 6,
+ E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE = 7
+} EHTMLEditorSelectionFontSize;
+
+typedef enum {
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER,
+ E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT
+} EHTMLEditorSelectionAlignment;
+
+typedef enum {
+ E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER,
+ E_HTML_EDITOR_SELECTION_GRANULARITY_WORD
+} EHTMLEditorSelectionGranularity;
+
+/**
+ * EHTMLEditorViewCommand:
+ * @E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR:
+ * Sets background color to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_BOLD:
+ * Toggles bold formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_COPY:
+ * Copies current selection to clipboard.
+ * @E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK:
+ * Converts current selection to a link that points to URL in value
+ * @E_HTML_EDITOR_VIEW_COMMAND_CUT:
+ * Cuts current selection to clipboard.
+ * @E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR:
+ * (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_DELETE:
+ * Deletes current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING:
+ * Highlights given string.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME:
+ * Sets font name to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE:
+ * Sets font point size to given value (no units, just number)
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA:
+ * Changes font size by given delta value (no units, just number)
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR:
+ * Sets font color to given value
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK:
+ * Sets block type of current paragraph to given format. Allowed formats
+ * are "BLOCKQUOTE", "H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE" and
+ * "ADDRESS".
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE:
+ * (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR:
+ * Sets color in which results of "FindString" command should be
+ * highlighted to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INDENT:
+ * Indents current paragraph by one level.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML:
+ * Inserts give HTML code into document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE:
+ * Inserts a horizontal rule (&lt;HR&gt;) on current line.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE:
+ * Inserts an image with given source file.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK:
+ * Breaks line at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT:
+ * Breaks citation at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST:
+ * Creates an ordered list environment at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH:
+ * Inserts a new paragraph at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT:
+ * Inserts given text at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST:
+ * Creates an undordered list environment at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_ITALIC:
+ * Toggles italic formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER:
+ * Aligns current paragraph to center.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL:
+ * Justifies current paragraph to block.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE:
+ * Removes any justification or alignment of current paragraph.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT:
+ * Aligns current paragraph to right.
+ * @E_HTML_EDITOR_VIEW_COMMAND_OUTDENT:
+ * Outdents current paragraph by one level.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE:
+ * Pastes clipboard content at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE:
+ * Pastes clipboard content and matches its style to style at current
+ * cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT:
+ * Pastes clipboard content at current cursor position removing any HTML
+ * formatting.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PRINT:
+ * Print current document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_REDO:
+ * Redoes last action.
+ * @E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT:
+ * Removes any formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL:
+ * Extends selects to the entire document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH:
+ * Toggles strikethrough formatting.
+ * @E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS:
+ * Toggles whether style should be defined in CSS "style" attribute of
+ * elements or whether to use deprecated &lt;FONT&gt; tags. Depends on
+ * whether given value is "true" or "false".
+ * @E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT:
+ * Toggles subscript of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT:
+ * Toggles superscript of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE:
+ * (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE:
+ * Toggles underline formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNDO:
+ * Undoes last action.
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNLINK:
+ * Removes active links (&lt;A&gt;) from current selection (if there's any).
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNSELECT:
+ * Cancels current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_USE_CSS:
+ * Whether to allow use of CSS or not depending on whether given value is
+ * "true" or "false".
+ *
+ * Specifies the DOM command to execute in e_editor_widget_exec_command().
+ * Some commands require value to be passed in, which is always stated in the
+ * documentation.
+ */
+typedef enum {
+ E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR,
+ E_HTML_EDITOR_VIEW_COMMAND_BOLD,
+ E_HTML_EDITOR_VIEW_COMMAND_COPY,
+ E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK,
+ E_HTML_EDITOR_VIEW_COMMAND_CUT,
+ E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR,
+ E_HTML_EDITOR_VIEW_COMMAND_DELETE,
+ E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING,
+ E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME,
+ E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE,
+ E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA,
+ E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR,
+ E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK,
+ E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE,
+ E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR,
+ E_HTML_EDITOR_VIEW_COMMAND_INDENT,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT,
+ E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST,
+ E_HTML_EDITOR_VIEW_COMMAND_ITALIC,
+ E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER,
+ E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL,
+ E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT,
+ E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE,
+ E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT,
+ E_HTML_EDITOR_VIEW_COMMAND_OUTDENT,
+ E_HTML_EDITOR_VIEW_COMMAND_PASTE,
+ E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE,
+ E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT,
+ E_HTML_EDITOR_VIEW_COMMAND_PRINT,
+ E_HTML_EDITOR_VIEW_COMMAND_REDO,
+ E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT,
+ E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL,
+ E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH,
+ E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS,
+ E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT,
+ E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT,
+ E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE,
+ E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE,
+ E_HTML_EDITOR_VIEW_COMMAND_UNDO,
+ E_HTML_EDITOR_VIEW_COMMAND_UNLINK,
+ E_HTML_EDITOR_VIEW_COMMAND_UNSELECT,
+ E_HTML_EDITOR_VIEW_COMMAND_USE_CSS
+} EHTMLEditorViewCommand;
+
+/**
+ * EImageLoadingPolicy:
+ * @E_IMAGE_LOADING_POLICY_NEVER:
+ * Never load images from a remote server.
+ * @E_IMAGE_LOADING_POLICY_SOMETIMES:
+ * Only load images from a remote server if the sender is a known contact.
+ * @E_IMAGE_LOADING_POLICY_ALWAYS:
+ * Always load images from a remote server.
+ *
+ * Policy for loading remote image URLs in email. Allowing images to be
+ * loaded from a remote server may have privacy implications.
+ **/
+typedef enum {
+ E_IMAGE_LOADING_POLICY_NEVER,
+ E_IMAGE_LOADING_POLICY_SOMETIMES,
+ E_IMAGE_LOADING_POLICY_ALWAYS
+} EImageLoadingPolicy;
+
G_END_DECLS
#endif /* E_UTIL_ENUMS_H */
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 784858e99f..e9b06fe27c 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -81,6 +81,8 @@
#include <e-util/e-client-cache.h>
#include <e-util/e-client-combo-box.h>
#include <e-util/e-client-selector.h>
+#include <e-util/e-color-chooser-widget.h>
+#include <e-util/e-color-combo.h>
#include <e-util/e-config.h>
#include <e-util/e-contact-store.h>
#include <e-util/e-data-capture.h>
@@ -89,6 +91,11 @@
#include <e-util/e-destination-store.h>
#include <e-util/e-dialog-utils.h>
#include <e-util/e-dialog-widgets.h>
+#include <e-util/e-emoticon-action.h>
+#include <e-util/e-emoticon-chooser-menu.h>
+#include <e-util/e-emoticon-chooser.h>
+#include <e-util/e-emoticon-tool-button.h>
+#include <e-util/e-emoticon.h>
#include <e-util/e-event.h>
#include <e-util/e-file-request.h>
#include <e-util/e-file-utils.h>
@@ -103,9 +110,27 @@
#include <e-util/e-filter-part.h>
#include <e-util/e-filter-rule.h>
#include <e-util/e-focus-tracker.h>
+#include <e-util/e-html-editor-actions.h>
+#include <e-util/e-html-editor-cell-dialog.h>
+#include <e-util/e-html-editor-dialog.h>
+#include <e-util/e-html-editor-find-dialog.h>
+#include <e-util/e-html-editor-hrule-dialog.h>
+#include <e-util/e-html-editor-image-dialog.h>
+#include <e-util/e-html-editor-link-dialog.h>
+#include <e-util/e-html-editor-page-dialog.h>
+#include <e-util/e-html-editor-paragraph-dialog.h>
+#include <e-util/e-html-editor-replace-dialog.h>
+#include <e-util/e-html-editor-selection.h>
+#include <e-util/e-html-editor-spell-check-dialog.h>
+#include <e-util/e-html-editor-table-dialog.h>
+#include <e-util/e-html-editor-text-dialog.h>
+#include <e-util/e-html-editor-utils.h>
+#include <e-util/e-html-editor-view.h>
+#include <e-util/e-html-editor.h>
#include <e-util/e-html-utils.h>
#include <e-util/e-icon-factory.h>
#include <e-util/e-image-chooser.h>
+#include <e-util/e-image-chooser-dialog.h>
#include <e-util/e-import-assistant.h>
#include <e-util/e-import.h>
#include <e-util/e-interval-chooser.h>
@@ -220,7 +245,6 @@
#include <e-util/e-url-entry.h>
#include <e-util/e-util-enums.h>
#include <e-util/e-util-enumtypes.h>
-#include <e-util/e-web-view-gtkhtml.h>
#include <e-util/e-web-view-preview.h>
#include <e-util/e-web-view.h>
#include <e-util/e-widget-undo.h>
diff --git a/e-util/e-web-view-gtkhtml.c b/e-util/e-web-view-gtkhtml.c
deleted file mode 100644
index 7963a1cbf9..0000000000
--- a/e-util/e-web-view-gtkhtml.c
+++ /dev/null
@@ -1,2352 +0,0 @@
-/*
- * e-web-view-gtkhtml.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-web-view-gtkhtml.h"
-
-#include <string.h>
-#include <glib/gi18n-lib.h>
-
-#include <camel/camel.h>
-#include <libebackend/libebackend.h>
-
-#include "e-alert-dialog.h"
-#include "e-alert-sink.h"
-#include "e-misc-utils.h"
-#include "e-plugin-ui.h"
-#include "e-popup-action.h"
-#include "e-selectable.h"
-
-#define E_WEB_VIEW_GTKHTML_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLPrivate))
-
-typedef struct _EWebViewGtkHTMLRequest EWebViewGtkHTMLRequest;
-
-struct _EWebViewGtkHTMLPrivate {
- GList *requests;
- GtkUIManager *ui_manager;
- gchar *selected_uri;
- GdkPixbufAnimation *cursor_image;
-
- GtkAction *open_proxy;
- GtkAction *print_proxy;
- GtkAction *save_as_proxy;
-
- GtkTargetList *copy_target_list;
- GtkTargetList *paste_target_list;
-
- /* Lockdown Options */
- guint disable_printing : 1;
- guint disable_save_to_disk : 1;
-};
-
-struct _EWebViewGtkHTMLRequest {
- GFile *file;
- EWebViewGtkHTML *web_view;
- GCancellable *cancellable;
- GInputStream *input_stream;
- GtkHTMLStream *output_stream;
- gchar buffer[4096];
-};
-
-enum {
- PROP_0,
- PROP_ANIMATE,
- PROP_CARET_MODE,
- PROP_COPY_TARGET_LIST,
- PROP_DISABLE_PRINTING,
- PROP_DISABLE_SAVE_TO_DISK,
- PROP_EDITABLE,
- PROP_INLINE_SPELLING,
- PROP_MAGIC_LINKS,
- PROP_MAGIC_SMILEYS,
- PROP_OPEN_PROXY,
- PROP_PASTE_TARGET_LIST,
- PROP_PRINT_PROXY,
- PROP_SAVE_AS_PROXY,
- PROP_SELECTED_URI,
- PROP_CURSOR_IMAGE
-};
-
-enum {
- COPY_CLIPBOARD,
- CUT_CLIPBOARD,
- PASTE_CLIPBOARD,
- POPUP_EVENT,
- STATUS_MESSAGE,
- STOP_LOADING,
- UPDATE_ACTIONS,
- PROCESS_MAILTO,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL];
-
-static const gchar *ui =
-"<ui>"
-" <popup name='context'>"
-" <menuitem action='copy-clipboard'/>"
-" <separator/>"
-" <placeholder name='custom-actions-1'>"
-" <menuitem action='open'/>"
-" <menuitem action='save-as'/>"
-" <menuitem action='http-open'/>"
-" <menuitem action='send-message'/>"
-" <menuitem action='print'/>"
-" </placeholder>"
-" <placeholder name='custom-actions-2'>"
-" <menuitem action='uri-copy'/>"
-" <menuitem action='mailto-copy'/>"
-" <menuitem action='image-copy'/>"
-" </placeholder>"
-" <placeholder name='custom-actions-3'/>"
-" <separator/>"
-" <menuitem action='select-all'/>"
-" </popup>"
-"</ui>";
-
-/* Forward Declarations */
-static void e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *iface);
-static void e_web_view_gtkhtml_selectable_init (ESelectableInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
- EWebViewGtkHTML,
- e_web_view_gtkhtml,
- GTK_TYPE_HTML,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_EXTENSIBLE, NULL)
- G_IMPLEMENT_INTERFACE (
- E_TYPE_ALERT_SINK,
- e_web_view_gtkhtml_alert_sink_init)
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SELECTABLE,
- e_web_view_gtkhtml_selectable_init))
-
-static EWebViewGtkHTMLRequest *
-web_view_gtkhtml_request_new (EWebViewGtkHTML *web_view,
- const gchar *uri,
- GtkHTMLStream *stream)
-{
- EWebViewGtkHTMLRequest *request;
- GList *list;
-
- request = g_slice_new (EWebViewGtkHTMLRequest);
-
- /* Try to detect file paths posing as URIs. */
- if (*uri == '/')
- request->file = g_file_new_for_path (uri);
- else
- request->file = g_file_new_for_uri (uri);
-
- request->web_view = g_object_ref (web_view);
- request->cancellable = g_cancellable_new ();
- request->input_stream = NULL;
- request->output_stream = stream;
-
- list = request->web_view->priv->requests;
- list = g_list_prepend (list, request);
- request->web_view->priv->requests = list;
-
- return request;
-}
-
-static void
-web_view_gtkhtml_request_free (EWebViewGtkHTMLRequest *request)
-{
- GList *list;
-
- list = request->web_view->priv->requests;
- list = g_list_remove (list, request);
- request->web_view->priv->requests = list;
-
- g_object_unref (request->file);
- g_object_unref (request->web_view);
- g_object_unref (request->cancellable);
-
- if (request->input_stream != NULL)
- g_object_unref (request->input_stream);
-
- g_slice_free (EWebViewGtkHTMLRequest, request);
-}
-
-static void
-web_view_gtkhtml_request_cancel (EWebViewGtkHTMLRequest *request)
-{
- g_cancellable_cancel (request->cancellable);
-}
-
-static gboolean
-web_view_gtkhtml_request_check_for_error (EWebViewGtkHTMLRequest *request,
- GError *error)
-{
- GtkHTML *html;
- GtkHTMLStream *stream;
-
- if (error == NULL)
- return FALSE;
-
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
- /* use this error, but do not close the stream */
- g_error_free (error);
- return TRUE;
- }
-
- /* XXX Should we log errors that are not cancellations? */
-
- html = GTK_HTML (request->web_view);
- stream = request->output_stream;
-
- gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR);
- web_view_gtkhtml_request_free (request);
- g_error_free (error);
-
- return TRUE;
-}
-
-static void
-web_view_gtkhtml_request_stream_read_cb (GInputStream *input_stream,
- GAsyncResult *result,
- EWebViewGtkHTMLRequest *request)
-{
- gssize bytes_read;
- GError *error = NULL;
-
- bytes_read = g_input_stream_read_finish (input_stream, result, &error);
-
- if (web_view_gtkhtml_request_check_for_error (request, error))
- return;
-
- if (bytes_read == 0) {
- gtk_html_end (
- GTK_HTML (request->web_view),
- request->output_stream, GTK_HTML_STREAM_OK);
- web_view_gtkhtml_request_free (request);
- return;
- }
-
- gtk_html_write (
- GTK_HTML (request->web_view),
- request->output_stream, request->buffer, bytes_read);
-
- g_input_stream_read_async (
- request->input_stream, request->buffer,
- sizeof (request->buffer), G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_gtkhtml_request_stream_read_cb, request);
-}
-
-static void
-web_view_gtkhtml_request_read_cb (GFile *file,
- GAsyncResult *result,
- EWebViewGtkHTMLRequest *request)
-{
- GFileInputStream *input_stream;
- GError *error = NULL;
-
- /* Input stream might be NULL, so don't use cast macro. */
- input_stream = g_file_read_finish (file, result, &error);
- request->input_stream = (GInputStream *) input_stream;
-
- if (web_view_gtkhtml_request_check_for_error (request, error))
- return;
-
- g_input_stream_read_async (
- request->input_stream, request->buffer,
- sizeof (request->buffer), G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_gtkhtml_request_stream_read_cb, request);
-}
-
-static void
-action_copy_clipboard_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- e_web_view_gtkhtml_copy_clipboard (web_view);
-}
-
-static void
-action_http_open_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- const gchar *uri;
- gpointer parent;
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- uri = e_web_view_gtkhtml_get_selected_uri (web_view);
- g_return_if_fail (uri != NULL);
-
- e_show_uri (parent, uri);
-}
-
-static void
-action_mailto_copy_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- CamelURL *curl;
- CamelInternetAddress *inet_addr;
- GtkClipboard *clipboard;
- const gchar *uri;
- gchar *text;
-
- uri = e_web_view_gtkhtml_get_selected_uri (web_view);
- g_return_if_fail (uri != NULL);
-
- /* This should work because we checked it in update_actions(). */
- curl = camel_url_new (uri, NULL);
- g_return_if_fail (curl != NULL);
-
- inet_addr = camel_internet_address_new ();
- camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
- text = camel_address_format (CAMEL_ADDRESS (inet_addr));
- if (text == NULL || *text == '\0')
- text = g_strdup (uri + strlen ("mailto:"));
-
- g_object_unref (inet_addr);
- camel_url_free (curl);
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
- gtk_clipboard_set_text (clipboard, text, -1);
- gtk_clipboard_store (clipboard);
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_text (clipboard, text, -1);
- gtk_clipboard_store (clipboard);
-
- g_free (text);
-}
-
-static void
-action_select_all_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- e_web_view_gtkhtml_select_all (web_view);
-}
-
-static void
-action_send_message_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- const gchar *uri;
- gpointer parent;
- gboolean handled;
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- uri = e_web_view_gtkhtml_get_selected_uri (web_view);
- g_return_if_fail (uri != NULL);
-
- handled = FALSE;
- g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled);
-
- if (!handled)
- e_show_uri (parent, uri);
-}
-
-static void
-action_uri_copy_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- GtkClipboard *clipboard;
- const gchar *uri;
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- uri = e_web_view_gtkhtml_get_selected_uri (web_view);
- g_return_if_fail (uri != NULL);
-
- gtk_clipboard_set_text (clipboard, uri, -1);
- gtk_clipboard_store (clipboard);
-}
-
-static void
-action_image_copy_cb (GtkAction *action,
- EWebViewGtkHTML *web_view)
-{
- GtkClipboard *clipboard;
- GdkPixbufAnimation *animation;
- GdkPixbuf *pixbuf;
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- animation = e_web_view_gtkhtml_get_cursor_image (web_view);
- g_return_if_fail (animation != NULL);
-
- pixbuf = gdk_pixbuf_animation_get_static_image (animation);
- if (!pixbuf)
- return;
-
- gtk_clipboard_set_image (clipboard, pixbuf);
- gtk_clipboard_store (clipboard);
-}
-
-static GtkActionEntry uri_entries[] = {
-
- { "uri-copy",
- "edit-copy",
- N_("_Copy Link Location"),
- "<Control>c",
- N_("Copy the link to the clipboard"),
- G_CALLBACK (action_uri_copy_cb) }
-};
-
-static GtkActionEntry http_entries[] = {
-
- { "http-open",
- "emblem-web",
- N_("_Open Link in Browser"),
- NULL,
- N_("Open the link in a web browser"),
- G_CALLBACK (action_http_open_cb) }
-};
-
-static GtkActionEntry mailto_entries[] = {
-
- { "mailto-copy",
- "edit-copy",
- N_("_Copy Email Address"),
- "<Control>c",
- N_("Copy the email address to the clipboard"),
- G_CALLBACK (action_mailto_copy_cb) },
-
- { "send-message",
- "mail-message-new",
- N_("_Send New Message To..."),
- NULL,
- N_("Send a mail message to this address"),
- G_CALLBACK (action_send_message_cb) }
-};
-
-static GtkActionEntry image_entries[] = {
-
- { "image-copy",
- "edit-copy",
- N_("_Copy Image"),
- "<Control>c",
- N_("Copy the image to the clipboard"),
- G_CALLBACK (action_image_copy_cb) }
-};
-
-static GtkActionEntry selection_entries[] = {
-
- { "copy-clipboard",
- "edit-copy",
- N_("_Copy"),
- "<Control>c",
- N_("Copy the selection"),
- G_CALLBACK (action_copy_clipboard_cb) },
-};
-
-static GtkActionEntry standard_entries[] = {
-
- { "select-all",
- "edit-select-all",
- N_("Select _All"),
- NULL,
- N_("Select all text and images"),
- G_CALLBACK (action_select_all_cb) }
-};
-
-static gboolean
-web_view_gtkhtml_button_press_event_cb (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkHTML *frame)
-{
- gboolean event_handled = FALSE;
- gchar *uri = NULL;
-
- if (event) {
- GdkPixbufAnimation *anim;
-
- if (frame == NULL)
- frame = GTK_HTML (web_view);
-
- anim = gtk_html_get_image_at (frame, event->x, event->y);
- e_web_view_gtkhtml_set_cursor_image (web_view, anim);
- if (anim != NULL)
- g_object_unref (anim);
- }
-
- if (event != NULL && event->button != 3)
- return FALSE;
-
- /* Only extract a URI if no selection is active. Selected text
- * implies the user is more likely to want to copy the selection
- * to the clipboard than open a link within the selection. */
- if (!e_web_view_gtkhtml_is_selection_active (web_view))
- uri = e_web_view_gtkhtml_extract_uri (web_view, event, frame);
-
- if (uri != NULL && g_str_has_prefix (uri, "##")) {
- g_free (uri);
- return FALSE;
- }
-
- g_signal_emit (
- web_view, signals[POPUP_EVENT], 0,
- event, uri, &event_handled);
-
- g_free (uri);
-
- return event_handled;
-}
-
-static void
-web_view_gtkhtml_menu_item_select_cb (EWebViewGtkHTML *web_view,
- GtkWidget *widget)
-{
- GtkAction *action;
- GtkActivatable *activatable;
- const gchar *tooltip;
-
- activatable = GTK_ACTIVATABLE (widget);
- action = gtk_activatable_get_related_action (activatable);
- tooltip = gtk_action_get_tooltip (action);
-
- if (tooltip == NULL)
- return;
-
- e_web_view_gtkhtml_status_message (web_view, tooltip);
-}
-
-static void
-web_view_gtkhtml_menu_item_deselect_cb (EWebViewGtkHTML *web_view)
-{
- e_web_view_gtkhtml_status_message (web_view, NULL);
-}
-
-static void
-web_view_gtkhtml_connect_proxy_cb (EWebViewGtkHTML *web_view,
- GtkAction *action,
- GtkWidget *proxy)
-{
- if (!GTK_IS_MENU_ITEM (proxy))
- return;
-
- g_signal_connect_swapped (
- proxy, "select",
- G_CALLBACK (web_view_gtkhtml_menu_item_select_cb), web_view);
-
- g_signal_connect_swapped (
- proxy, "deselect",
- G_CALLBACK (web_view_gtkhtml_menu_item_deselect_cb), web_view);
-}
-
-static void
-web_view_gtkhtml_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_ANIMATE:
- e_web_view_gtkhtml_set_animate (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_CARET_MODE:
- e_web_view_gtkhtml_set_caret_mode (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_DISABLE_PRINTING:
- e_web_view_gtkhtml_set_disable_printing (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_DISABLE_SAVE_TO_DISK:
- e_web_view_gtkhtml_set_disable_save_to_disk (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_EDITABLE:
- e_web_view_gtkhtml_set_editable (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_INLINE_SPELLING:
- e_web_view_gtkhtml_set_inline_spelling (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_MAGIC_LINKS:
- e_web_view_gtkhtml_set_magic_links (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_MAGIC_SMILEYS:
- e_web_view_gtkhtml_set_magic_smileys (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_OPEN_PROXY:
- e_web_view_gtkhtml_set_open_proxy (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_object (value));
- return;
-
- case PROP_PRINT_PROXY:
- e_web_view_gtkhtml_set_print_proxy (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_object (value));
- return;
-
- case PROP_SAVE_AS_PROXY:
- e_web_view_gtkhtml_set_save_as_proxy (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_object (value));
- return;
-
- case PROP_SELECTED_URI:
- e_web_view_gtkhtml_set_selected_uri (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_string (value));
- return;
- case PROP_CURSOR_IMAGE:
- e_web_view_gtkhtml_set_cursor_image (
- E_WEB_VIEW_GTKHTML (object),
- g_value_get_object (value));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-web_view_gtkhtml_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_ANIMATE:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_animate (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_CARET_MODE:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_caret_mode (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_COPY_TARGET_LIST:
- g_value_set_boxed (
- value, e_web_view_gtkhtml_get_copy_target_list (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_DISABLE_PRINTING:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_disable_printing (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_DISABLE_SAVE_TO_DISK:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_disable_save_to_disk (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_EDITABLE:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_editable (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_INLINE_SPELLING:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_inline_spelling (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_MAGIC_LINKS:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_magic_links (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_MAGIC_SMILEYS:
- g_value_set_boolean (
- value, e_web_view_gtkhtml_get_magic_smileys (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_OPEN_PROXY:
- g_value_set_object (
- value, e_web_view_gtkhtml_get_open_proxy (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_PASTE_TARGET_LIST:
- g_value_set_boxed (
- value, e_web_view_gtkhtml_get_paste_target_list (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_PRINT_PROXY:
- g_value_set_object (
- value, e_web_view_gtkhtml_get_print_proxy (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_SAVE_AS_PROXY:
- g_value_set_object (
- value, e_web_view_gtkhtml_get_save_as_proxy (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_SELECTED_URI:
- g_value_set_string (
- value, e_web_view_gtkhtml_get_selected_uri (
- E_WEB_VIEW_GTKHTML (object)));
- return;
-
- case PROP_CURSOR_IMAGE:
- g_value_set_object (
- value, e_web_view_gtkhtml_get_cursor_image (
- E_WEB_VIEW_GTKHTML (object)));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-web_view_gtkhtml_dispose (GObject *object)
-{
- EWebViewGtkHTMLPrivate *priv;
-
- priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
-
- if (priv->ui_manager != NULL) {
- g_object_unref (priv->ui_manager);
- priv->ui_manager = NULL;
- }
-
- if (priv->open_proxy != NULL) {
- g_object_unref (priv->open_proxy);
- priv->open_proxy = NULL;
- }
-
- if (priv->print_proxy != NULL) {
- g_object_unref (priv->print_proxy);
- priv->print_proxy = NULL;
- }
-
- if (priv->save_as_proxy != NULL) {
- g_object_unref (priv->save_as_proxy);
- priv->save_as_proxy = NULL;
- }
-
- if (priv->copy_target_list != NULL) {
- gtk_target_list_unref (priv->copy_target_list);
- priv->copy_target_list = NULL;
- }
-
- if (priv->paste_target_list != NULL) {
- gtk_target_list_unref (priv->paste_target_list);
- priv->paste_target_list = NULL;
- }
-
- if (priv->cursor_image != NULL) {
- g_object_unref (priv->cursor_image);
- priv->cursor_image = NULL;
- }
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->dispose (object);
-}
-
-static void
-web_view_gtkhtml_finalize (GObject *object)
-{
- EWebViewGtkHTMLPrivate *priv;
-
- priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
-
- /* All URI requests should be complete or cancelled by now. */
- if (priv->requests != NULL)
- g_warning ("Finalizing EWebViewGtkHTML with active URI requests");
-
- g_free (priv->selected_uri);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->finalize (object);
-}
-
-static void
-web_view_gtkhtml_constructed (GObject *object)
-{
-#ifndef G_OS_WIN32
- GSettings *settings;
-
- settings = g_settings_new ("org.gnome.desktop.lockdown");
-
- g_settings_bind (
- settings, "disable-printing",
- object, "disable-printing",
- G_SETTINGS_BIND_GET);
-
- g_settings_bind (
- settings, "disable-save-to-disk",
- object, "disable-save-to-disk",
- G_SETTINGS_BIND_GET);
-
- g_object_unref (settings);
-#endif
-
- /* Chain up to parent's constructed() method. */
- G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->constructed (object);
-}
-
-static gboolean
-web_view_gtkhtml_button_press_event (GtkWidget *widget,
- GdkEventButton *event)
-{
- GtkWidgetClass *widget_class;
- EWebViewGtkHTML *web_view;
-
- web_view = E_WEB_VIEW_GTKHTML (widget);
-
- if (web_view_gtkhtml_button_press_event_cb (web_view, event, NULL))
- return TRUE;
-
- /* Chain up to parent's button_press_event() method. */
- widget_class = GTK_WIDGET_CLASS (e_web_view_gtkhtml_parent_class);
- return widget_class->button_press_event (widget, event);
-}
-
-static gboolean
-web_view_gtkhtml_scroll_event (GtkWidget *widget,
- GdkEventScroll *event)
-{
- if (event->state & GDK_CONTROL_MASK) {
- GdkScrollDirection direction = event->direction;
-
- if (direction == GDK_SCROLL_SMOOTH) {
- static gdouble total_delta_y = 0.0;
-
- total_delta_y += event->delta_y;
-
- if (total_delta_y >= 1.0) {
- total_delta_y = 0.0;
- direction = GDK_SCROLL_DOWN;
- } else if (total_delta_y <= -1.0) {
- total_delta_y = 0.0;
- direction = GDK_SCROLL_UP;
- } else {
- return FALSE;
- }
- }
-
- switch (direction) {
- case GDK_SCROLL_UP:
- gtk_html_zoom_in (GTK_HTML (widget));
- return TRUE;
- case GDK_SCROLL_DOWN:
- gtk_html_zoom_out (GTK_HTML (widget));
- return TRUE;
- default:
- break;
- }
- }
-
- return FALSE;
-}
-
-static void
-web_view_gtkhtml_url_requested (GtkHTML *html,
- const gchar *uri,
- GtkHTMLStream *stream)
-{
- EWebViewGtkHTMLRequest *request;
-
- request = web_view_gtkhtml_request_new (E_WEB_VIEW_GTKHTML (html), uri, stream);
-
- g_file_read_async (
- request->file, G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_gtkhtml_request_read_cb, request);
-}
-
-static void
-web_view_gtkhtml_gtkhtml_link_clicked (GtkHTML *html,
- const gchar *uri)
-{
- EWebViewGtkHTMLClass *class;
- EWebViewGtkHTML *web_view;
-
- web_view = E_WEB_VIEW_GTKHTML (html);
-
- class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
- g_return_if_fail (class->link_clicked != NULL);
-
- class->link_clicked (web_view, uri);
-}
-
-static void
-web_view_gtkhtml_on_url (GtkHTML *html,
- const gchar *uri)
-{
- EWebViewGtkHTMLClass *class;
- EWebViewGtkHTML *web_view;
-
- web_view = E_WEB_VIEW_GTKHTML (html);
-
- class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
- g_return_if_fail (class->hovering_over_link != NULL);
-
- /* XXX WebKit would supply a title here. */
- class->hovering_over_link (web_view, NULL, uri);
-}
-
-static void
-web_view_gtkhtml_iframe_created (GtkHTML *html,
- GtkHTML *iframe)
-{
- g_signal_connect_swapped (
- iframe, "button-press-event",
- G_CALLBACK (web_view_gtkhtml_button_press_event_cb), html);
-}
-
-static gchar *
-web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkHTML *html)
-{
- gchar *uri;
-
- if (event != NULL)
- uri = gtk_html_get_url_at (html, event->x, event->y);
- else
- uri = gtk_html_get_cursor_url (html);
-
- return uri;
-}
-
-static void
-web_view_gtkhtml_hovering_over_link (EWebViewGtkHTML *web_view,
- const gchar *title,
- const gchar *uri)
-{
- CamelInternetAddress *address;
- CamelURL *curl;
- const gchar *format = NULL;
- gchar *message = NULL;
- gchar *who;
-
- if (uri == NULL || *uri == '\0')
- goto exit;
-
- if (g_str_has_prefix (uri, "mailto:"))
- format = _("Click to mail %s");
- else if (g_str_has_prefix (uri, "callto:") ||
- g_str_has_prefix (uri, "h323:") ||
- g_str_has_prefix (uri, "sip:") ||
- g_str_has_prefix (uri, "tel:"))
- format = _("Click to call %s");
- else if (g_str_has_prefix (uri, "##"))
- message = g_strdup (_("Click to hide/unhide addresses"));
- else
- message = g_strdup_printf (_("Click to open %s"), uri);
-
- if (format == NULL)
- goto exit;
-
- /* XXX Use something other than Camel here. Surely
- * there's other APIs around that can do this. */
- curl = camel_url_new (uri, NULL);
- address = camel_internet_address_new ();
- camel_address_decode (CAMEL_ADDRESS (address), curl->path);
- who = camel_address_format (CAMEL_ADDRESS (address));
- g_object_unref (address);
- camel_url_free (curl);
-
- if (who == NULL)
- who = g_strdup (strchr (uri, ':') + 1);
-
- message = g_strdup_printf (format, who);
-
- g_free (who);
-
-exit:
- e_web_view_gtkhtml_status_message (web_view, message);
-
- g_free (message);
-}
-
-static void
-web_view_gtkhtml_link_clicked (EWebViewGtkHTML *web_view,
- const gchar *uri)
-{
- gpointer parent;
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- e_show_uri (parent, uri);
-}
-
-static void
-web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
- const gchar *string)
-{
- if (string != NULL && *string != '\0')
- gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
- else
- e_web_view_gtkhtml_clear (web_view);
-}
-
-static void
-web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
-{
- gtk_html_command (GTK_HTML (web_view), "copy");
-}
-
-static void
-web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
-{
- if (e_web_view_gtkhtml_get_editable (web_view))
- gtk_html_command (GTK_HTML (web_view), "cut");
-}
-
-static void
-web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
-{
- if (e_web_view_gtkhtml_get_editable (web_view))
- gtk_html_command (GTK_HTML (web_view), "paste");
-}
-
-static gboolean
-web_view_gtkhtml_popup_event (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- const gchar *uri)
-{
- e_web_view_gtkhtml_set_selected_uri (web_view, uri);
- e_web_view_gtkhtml_show_popup_menu (web_view, event, NULL, NULL);
-
- return TRUE;
-}
-
-static void
-web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
-{
- g_list_foreach (
- web_view->priv->requests, (GFunc)
- web_view_gtkhtml_request_cancel, NULL);
-
- gtk_html_stop (GTK_HTML (web_view));
-}
-
-static void
-web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
-{
- GtkActionGroup *action_group;
- gboolean have_selection;
- gboolean scheme_is_http = FALSE;
- gboolean scheme_is_mailto = FALSE;
- gboolean uri_is_valid = FALSE;
- gboolean has_cursor_image;
- gboolean visible;
- const gchar *group_name;
- const gchar *uri;
-
- uri = e_web_view_gtkhtml_get_selected_uri (web_view);
- have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
- has_cursor_image = e_web_view_gtkhtml_get_cursor_image (web_view) != NULL;
-
- /* Parse the URI early so we know if the actions will work. */
- if (uri != NULL) {
- CamelURL *curl;
-
- curl = camel_url_new (uri, NULL);
- uri_is_valid = (curl != NULL);
- camel_url_free (curl);
-
- scheme_is_http =
- (g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
- (g_ascii_strncasecmp (uri, "https:", 6) == 0);
-
- scheme_is_mailto =
- (g_ascii_strncasecmp (uri, "mailto:", 7) == 0);
- }
-
- /* Allow copying the URI even if it's malformed. */
- group_name = "uri";
- visible = (uri != NULL) && !scheme_is_mailto;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "http";
- visible = uri_is_valid && scheme_is_http;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "mailto";
- visible = uri_is_valid && scheme_is_mailto;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "image";
- visible = has_cursor_image;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "selection";
- visible = have_selection;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "standard";
- visible = (uri == NULL);
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "lockdown-printing";
- visible = (uri == NULL) && !web_view->priv->disable_printing;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-
- group_name = "lockdown-save-to-disk";
- visible = (uri == NULL) && !web_view->priv->disable_save_to_disk;
- action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
-}
-
-static void
-web_view_gtkhtml_submit_alert (EAlertSink *alert_sink,
- EAlert *alert)
-{
- GtkIconInfo *icon_info;
- EWebViewGtkHTML *web_view;
- GtkWidget *dialog;
- GString *buffer;
- const gchar *icon_name = NULL;
- const gchar *filename;
- gpointer parent;
- gchar *icon_uri;
- gint size = 0;
- GError *error = NULL;
-
- web_view = E_WEB_VIEW_GTKHTML (alert_sink);
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- /* We use equivalent named icons instead of stock IDs,
- * since it's easier to get the filename of the icon. */
- switch (e_alert_get_message_type (alert)) {
- case GTK_MESSAGE_INFO:
- icon_name = "dialog-information";
- break;
-
- case GTK_MESSAGE_WARNING:
- icon_name = "dialog-warning";
- break;
-
- case GTK_MESSAGE_ERROR:
- icon_name = "dialog-error";
- break;
-
- default:
- dialog = e_alert_dialog_new (parent, alert);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
- return;
- }
-
- gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &size, NULL);
-
- icon_info = gtk_icon_theme_lookup_icon (
- gtk_icon_theme_get_default (),
- icon_name, size, GTK_ICON_LOOKUP_NO_SVG);
- g_return_if_fail (icon_info != NULL);
-
- filename = gtk_icon_info_get_filename (icon_info);
- icon_uri = g_filename_to_uri (filename, NULL, &error);
-
- if (error != NULL) {
- g_warning ("%s", error->message);
- g_clear_error (&error);
- }
-
- buffer = g_string_sized_new (512);
-
- g_string_append (
- buffer,
- "<html>"
- "<head>"
- "<meta http-equiv=\"content-type\""
- " content=\"text/html; charset=utf-8\">"
- "</head>"
- "<body>");
-
- g_string_append (
- buffer,
- "<table bgcolor='#000000' width='100%'"
- " cellpadding='1' cellspacing='0'>"
- "<tr>"
- "<td>"
- "<table bgcolor='#dddddd' width='100%' cellpadding='6'>"
- "<tr>");
-
- g_string_append_printf (
- buffer,
- "<tr>"
- "<td valign='top'>"
- "<img src='%s'/>"
- "</td>"
- "<td align='left' width='100%%'>"
- "<h3>%s</h3>"
- "%s"
- "</td>"
- "</tr>",
- icon_uri,
- e_alert_get_primary_text (alert),
- e_alert_get_secondary_text (alert));
-
- g_string_append (
- buffer,
- "</table>"
- "</td>"
- "</tr>"
- "</table>"
- "</body>"
- "</html>");
-
- e_web_view_gtkhtml_load_string (web_view, buffer->str);
-
- g_string_free (buffer, TRUE);
-
- gtk_icon_info_free (icon_info);
- g_free (icon_uri);
-}
-
-static void
-web_view_gtkhtml_selectable_update_actions (ESelectable *selectable,
- EFocusTracker *focus_tracker,
- GdkAtom *clipboard_targets,
- gint n_clipboard_targets)
-{
- EWebViewGtkHTML *web_view;
- GtkAction *action;
- /*GtkTargetList *target_list;*/
- gboolean can_paste = FALSE;
- gboolean editable;
- gboolean have_selection;
- gboolean sensitive;
- const gchar *tooltip;
- /*gint ii;*/
-
- web_view = E_WEB_VIEW_GTKHTML (selectable);
- editable = e_web_view_gtkhtml_get_editable (web_view);
- have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
-
- /* XXX GtkHtml implements its own clipboard instead of using
- * GDK_SELECTION_CLIPBOARD, so we don't get notifications
- * when the clipboard contents change. The logic below
- * is what we would do if GtkHtml worked properly.
- * Instead, we need to keep the Paste action sensitive so
- * its accelerator overrides GtkHtml's key binding. */
-#if 0
- target_list = e_selectable_get_paste_target_list (selectable);
- for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
- can_paste = gtk_target_list_find (
- target_list, clipboard_targets[ii], NULL);
-#endif
- can_paste = TRUE;
-
- action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
- sensitive = editable && have_selection;
- tooltip = _("Cut the selection");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
-
- action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
- sensitive = have_selection;
- tooltip = _("Copy the selection");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
-
- action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
- sensitive = editable && can_paste;
- tooltip = _("Paste the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
-
- action = e_focus_tracker_get_select_all_action (focus_tracker);
- sensitive = TRUE;
- tooltip = _("Select all text and images");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
-}
-
-static void
-web_view_gtkhtml_selectable_cut_clipboard (ESelectable *selectable)
-{
- e_web_view_gtkhtml_cut_clipboard (E_WEB_VIEW_GTKHTML (selectable));
-}
-
-static void
-web_view_gtkhtml_selectable_copy_clipboard (ESelectable *selectable)
-{
- e_web_view_gtkhtml_copy_clipboard (E_WEB_VIEW_GTKHTML (selectable));
-}
-
-static void
-web_view_gtkhtml_selectable_paste_clipboard (ESelectable *selectable)
-{
- e_web_view_gtkhtml_paste_clipboard (E_WEB_VIEW_GTKHTML (selectable));
-}
-
-static void
-web_view_gtkhtml_selectable_select_all (ESelectable *selectable)
-{
- e_web_view_gtkhtml_select_all (E_WEB_VIEW_GTKHTML (selectable));
-}
-
-static void
-e_web_view_gtkhtml_class_init (EWebViewGtkHTMLClass *class)
-{
- GObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkHTMLClass *html_class;
-
- g_type_class_add_private (class, sizeof (EWebViewGtkHTMLPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->set_property = web_view_gtkhtml_set_property;
- object_class->get_property = web_view_gtkhtml_get_property;
- object_class->dispose = web_view_gtkhtml_dispose;
- object_class->finalize = web_view_gtkhtml_finalize;
- object_class->constructed = web_view_gtkhtml_constructed;
-
- widget_class = GTK_WIDGET_CLASS (class);
- widget_class->button_press_event = web_view_gtkhtml_button_press_event;
- widget_class->scroll_event = web_view_gtkhtml_scroll_event;
-
- html_class = GTK_HTML_CLASS (class);
- html_class->url_requested = web_view_gtkhtml_url_requested;
- html_class->link_clicked = web_view_gtkhtml_gtkhtml_link_clicked;
- html_class->on_url = web_view_gtkhtml_on_url;
- html_class->iframe_created = web_view_gtkhtml_iframe_created;
-
- class->extract_uri = web_view_gtkhtml_extract_uri;
- class->hovering_over_link = web_view_gtkhtml_hovering_over_link;
- class->link_clicked = web_view_gtkhtml_link_clicked;
- class->load_string = web_view_gtkhtml_load_string;
- class->copy_clipboard = web_view_gtkhtml_copy_clipboard;
- class->cut_clipboard = web_view_gtkhtml_cut_clipboard;
- class->paste_clipboard = web_view_gtkhtml_paste_clipboard;
- class->popup_event = web_view_gtkhtml_popup_event;
- class->stop_loading = web_view_gtkhtml_stop_loading;
- class->update_actions = web_view_gtkhtml_update_actions;
-
- g_object_class_install_property (
- object_class,
- PROP_ANIMATE,
- g_param_spec_boolean (
- "animate",
- "Animate Images",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_CARET_MODE,
- g_param_spec_boolean (
- "caret-mode",
- "Caret Mode",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- /* Inherited from ESelectableInterface */
- g_object_class_override_property (
- object_class,
- PROP_COPY_TARGET_LIST,
- "copy-target-list");
-
- g_object_class_install_property (
- object_class,
- PROP_DISABLE_PRINTING,
- g_param_spec_boolean (
- "disable-printing",
- "Disable Printing",
- NULL,
- FALSE,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
-
- g_object_class_install_property (
- object_class,
- PROP_DISABLE_SAVE_TO_DISK,
- g_param_spec_boolean (
- "disable-save-to-disk",
- "Disable Save-to-Disk",
- NULL,
- FALSE,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
-
- g_object_class_install_property (
- object_class,
- PROP_EDITABLE,
- g_param_spec_boolean (
- "editable",
- "Editable",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_INLINE_SPELLING,
- g_param_spec_boolean (
- "inline-spelling",
- "Inline Spelling",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_MAGIC_LINKS,
- g_param_spec_boolean (
- "magic-links",
- "Magic Links",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_MAGIC_SMILEYS,
- g_param_spec_boolean (
- "magic-smileys",
- "Magic Smileys",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_OPEN_PROXY,
- g_param_spec_object (
- "open-proxy",
- "Open Proxy",
- NULL,
- GTK_TYPE_ACTION,
- G_PARAM_READWRITE));
-
- /* Inherited from ESelectableInterface */
- g_object_class_override_property (
- object_class,
- PROP_PASTE_TARGET_LIST,
- "paste-target-list");
-
- g_object_class_install_property (
- object_class,
- PROP_PRINT_PROXY,
- g_param_spec_object (
- "print-proxy",
- "Print Proxy",
- NULL,
- GTK_TYPE_ACTION,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_SAVE_AS_PROXY,
- g_param_spec_object (
- "save-as-proxy",
- "Save As Proxy",
- NULL,
- GTK_TYPE_ACTION,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_SELECTED_URI,
- g_param_spec_string (
- "selected-uri",
- "Selected URI",
- NULL,
- NULL,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_CURSOR_IMAGE,
- g_param_spec_object (
- "cursor-image",
- "Image animation at the mouse cursor",
- NULL,
- GDK_TYPE_PIXBUF_ANIMATION,
- G_PARAM_READWRITE));
-
- signals[COPY_CLIPBOARD] = g_signal_new (
- "copy-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, copy_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[CUT_CLIPBOARD] = g_signal_new (
- "cut-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, cut_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[PASTE_CLIPBOARD] = g_signal_new (
- "paste-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, paste_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[POPUP_EVENT] = g_signal_new (
- "popup-event",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, popup_event),
- g_signal_accumulator_true_handled, NULL,
- e_marshal_BOOLEAN__BOXED_STRING,
- G_TYPE_BOOLEAN, 2,
- GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
- G_TYPE_STRING);
-
- signals[STATUS_MESSAGE] = g_signal_new (
- "status-message",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, status_message),
- NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1,
- G_TYPE_STRING);
-
- signals[STOP_LOADING] = g_signal_new (
- "stop-loading",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, stop_loading),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[UPDATE_ACTIONS] = g_signal_new (
- "update-actions",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, update_actions),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- /* return TRUE when a signal handler processed the mailto URI */
- signals[PROCESS_MAILTO] = g_signal_new (
- "process-mailto",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EWebViewGtkHTMLClass, process_mailto),
- NULL, NULL,
- e_marshal_BOOLEAN__STRING,
- G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
-}
-
-static void
-e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *iface)
-{
- iface->submit_alert = web_view_gtkhtml_submit_alert;
-}
-
-static void
-e_web_view_gtkhtml_selectable_init (ESelectableInterface *iface)
-{
- iface->update_actions = web_view_gtkhtml_selectable_update_actions;
- iface->cut_clipboard = web_view_gtkhtml_selectable_cut_clipboard;
- iface->copy_clipboard = web_view_gtkhtml_selectable_copy_clipboard;
- iface->paste_clipboard = web_view_gtkhtml_selectable_paste_clipboard;
- iface->select_all = web_view_gtkhtml_selectable_select_all;
-}
-
-static void
-e_web_view_gtkhtml_init (EWebViewGtkHTML *web_view)
-{
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GtkTargetList *target_list;
- EPopupAction *popup_action;
- const gchar *domain = GETTEXT_PACKAGE;
- const gchar *id;
- GError *error = NULL;
-
- web_view->priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (web_view);
-
- ui_manager = gtk_ui_manager_new ();
- web_view->priv->ui_manager = ui_manager;
-
- g_signal_connect_swapped (
- ui_manager, "connect-proxy",
- G_CALLBACK (web_view_gtkhtml_connect_proxy_cb), web_view);
-
- target_list = gtk_target_list_new (NULL, 0);
- web_view->priv->copy_target_list = target_list;
-
- target_list = gtk_target_list_new (NULL, 0);
- web_view->priv->paste_target_list = target_list;
-
- action_group = gtk_action_group_new ("uri");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, uri_entries,
- G_N_ELEMENTS (uri_entries), web_view);
-
- action_group = gtk_action_group_new ("http");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, http_entries,
- G_N_ELEMENTS (http_entries), web_view);
-
- action_group = gtk_action_group_new ("mailto");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, mailto_entries,
- G_N_ELEMENTS (mailto_entries), web_view);
-
- action_group = gtk_action_group_new ("image");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, image_entries,
- G_N_ELEMENTS (image_entries), web_view);
-
- action_group = gtk_action_group_new ("selection");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, selection_entries,
- G_N_ELEMENTS (selection_entries), web_view);
-
- action_group = gtk_action_group_new ("standard");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, standard_entries,
- G_N_ELEMENTS (standard_entries), web_view);
-
- popup_action = e_popup_action_new ("open");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- g_object_bind_property (
- web_view, "open-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- /* Support lockdown. */
-
- action_group = gtk_action_group_new ("lockdown-printing");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- popup_action = e_popup_action_new ("print");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- g_object_bind_property (
- web_view, "print-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- action_group = gtk_action_group_new ("lockdown-save-to-disk");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- popup_action = e_popup_action_new ("save-as");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- g_object_bind_property (
- web_view, "save-as-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
-
- id = "org.gnome.evolution.webview";
- e_plugin_ui_register_manager (ui_manager, id, web_view);
- e_plugin_ui_enable_manager (ui_manager, id);
-
- e_extensible_load_extensions (E_EXTENSIBLE (web_view));
-}
-
-GtkWidget *
-e_web_view_gtkhtml_new (void)
-{
- return g_object_new (E_TYPE_WEB_VIEW_GTKHTML, NULL);
-}
-
-void
-e_web_view_gtkhtml_clear (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_load_empty (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
- const gchar *string)
-{
- EWebViewGtkHTMLClass *class;
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
- g_return_if_fail (class->load_string != NULL);
-
- class->load_string (web_view, string);
-}
-
-gboolean
-e_web_view_gtkhtml_get_animate (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_animate(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_animate (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_animate (EWebViewGtkHTML *web_view,
- gboolean animate)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_animate()
- * so we can get a "notify::animate" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_animate (GTK_HTML (web_view)) == animate)
- return;
-
- gtk_html_set_animate (GTK_HTML (web_view), animate);
-
- g_object_notify (G_OBJECT (web_view), "animate");
-}
-
-gboolean
-e_web_view_gtkhtml_get_caret_mode (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_caret_mode(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_caret_mode (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_caret_mode (EWebViewGtkHTML *web_view,
- gboolean caret_mode)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_caret_mode()
- * so we can get a "notify::caret-mode" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_caret_mode (GTK_HTML (web_view)) == caret_mode)
- return;
-
- gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode);
-
- g_object_notify (G_OBJECT (web_view), "caret-mode");
-}
-
-GtkTargetList *
-e_web_view_gtkhtml_get_copy_target_list (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->copy_target_list;
-}
-
-gboolean
-e_web_view_gtkhtml_get_disable_printing (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return web_view->priv->disable_printing;
-}
-
-void
-e_web_view_gtkhtml_set_disable_printing (EWebViewGtkHTML *web_view,
- gboolean disable_printing)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->disable_printing == disable_printing)
- return;
-
- web_view->priv->disable_printing = disable_printing;
-
- g_object_notify (G_OBJECT (web_view), "disable-printing");
-}
-
-gboolean
-e_web_view_gtkhtml_get_disable_save_to_disk (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return web_view->priv->disable_save_to_disk;
-}
-
-void
-e_web_view_gtkhtml_set_disable_save_to_disk (EWebViewGtkHTML *web_view,
- gboolean disable_save_to_disk)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->disable_save_to_disk == disable_save_to_disk)
- return;
-
- web_view->priv->disable_save_to_disk = disable_save_to_disk;
-
- g_object_notify (G_OBJECT (web_view), "disable-save-to-disk");
-}
-
-gboolean
-e_web_view_gtkhtml_get_editable (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_editable(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_editable (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_editable (EWebViewGtkHTML *web_view,
- gboolean editable)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_editable()
- * so we can get a "notify::editable" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_editable (GTK_HTML (web_view)) == editable)
- return;
-
- gtk_html_set_editable (GTK_HTML (web_view), editable);
-
- g_object_notify (G_OBJECT (web_view), "editable");
-}
-
-gboolean
-e_web_view_gtkhtml_get_inline_spelling (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_inline_spelling(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_inline_spelling (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_inline_spelling (EWebViewGtkHTML *web_view,
- gboolean inline_spelling)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_inline_spelling()
- * so we get a "notify::inline-spelling" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_inline_spelling (GTK_HTML (web_view)) == inline_spelling)
- return;
-
- gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling);
-
- g_object_notify (G_OBJECT (web_view), "inline-spelling");
-}
-
-gboolean
-e_web_view_gtkhtml_get_magic_links (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_magic_links(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_magic_links (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_magic_links (EWebViewGtkHTML *web_view,
- gboolean magic_links)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_magic_links()
- * so we can get a "notify::magic-links" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_magic_links (GTK_HTML (web_view)) == magic_links)
- return;
-
- gtk_html_set_magic_links (GTK_HTML (web_view), magic_links);
-
- g_object_notify (G_OBJECT (web_view), "magic-links");
-}
-
-gboolean
-e_web_view_gtkhtml_get_magic_smileys (EWebViewGtkHTML *web_view)
-{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_magic_smileys(). */
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_get_magic_smileys (GTK_HTML (web_view));
-}
-
-void
-e_web_view_gtkhtml_set_magic_smileys (EWebViewGtkHTML *web_view,
- gboolean magic_smileys)
-{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_magic_smileys()
- * so we can get a "notify::magic-smileys" signal. */
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (gtk_html_get_magic_smileys (GTK_HTML (web_view)) == magic_smileys)
- return;
-
- gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys);
-
- g_object_notify (G_OBJECT (web_view), "magic-smileys");
-}
-
-const gchar *
-e_web_view_gtkhtml_get_selected_uri (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->selected_uri;
-}
-
-void
-e_web_view_gtkhtml_set_selected_uri (EWebViewGtkHTML *web_view,
- const gchar *selected_uri)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (g_strcmp0 (web_view->priv->selected_uri, selected_uri) == 0)
- return;
-
- g_free (web_view->priv->selected_uri);
- web_view->priv->selected_uri = g_strdup (selected_uri);
-
- g_object_notify (G_OBJECT (web_view), "selected-uri");
-}
-
-GdkPixbufAnimation *
-e_web_view_gtkhtml_get_cursor_image (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->cursor_image;
-}
-
-void
-e_web_view_gtkhtml_set_cursor_image (EWebViewGtkHTML *web_view,
- GdkPixbufAnimation *image)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->cursor_image == image)
- return;
-
- if (image != NULL)
- g_object_ref (image);
-
- if (web_view->priv->cursor_image != NULL)
- g_object_unref (web_view->priv->cursor_image);
-
- web_view->priv->cursor_image = image;
-
- g_object_notify (G_OBJECT (web_view), "cursor-image");
-}
-
-GtkAction *
-e_web_view_gtkhtml_get_open_proxy (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->open_proxy;
-}
-
-void
-e_web_view_gtkhtml_set_open_proxy (EWebViewGtkHTML *web_view,
- GtkAction *open_proxy)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->open_proxy == open_proxy)
- return;
-
- if (open_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (open_proxy));
- g_object_ref (open_proxy);
- }
-
- if (web_view->priv->open_proxy != NULL)
- g_object_unref (web_view->priv->open_proxy);
-
- web_view->priv->open_proxy = open_proxy;
-
- g_object_notify (G_OBJECT (web_view), "open-proxy");
-}
-
-GtkTargetList *
-e_web_view_gtkhtml_get_paste_target_list (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->paste_target_list;
-}
-
-GtkAction *
-e_web_view_gtkhtml_get_print_proxy (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->print_proxy;
-}
-
-void
-e_web_view_gtkhtml_set_print_proxy (EWebViewGtkHTML *web_view,
- GtkAction *print_proxy)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->print_proxy == print_proxy)
- return;
-
- if (print_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (print_proxy));
- g_object_ref (print_proxy);
- }
-
- if (web_view->priv->print_proxy != NULL)
- g_object_unref (web_view->priv->print_proxy);
-
- web_view->priv->print_proxy = print_proxy;
-
- g_object_notify (G_OBJECT (web_view), "print-proxy");
-}
-
-GtkAction *
-e_web_view_gtkhtml_get_save_as_proxy (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->save_as_proxy;
-}
-
-void
-e_web_view_gtkhtml_set_save_as_proxy (EWebViewGtkHTML *web_view,
- GtkAction *save_as_proxy)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- if (web_view->priv->save_as_proxy == save_as_proxy)
- return;
-
- if (save_as_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (save_as_proxy));
- g_object_ref (save_as_proxy);
- }
-
- if (web_view->priv->save_as_proxy != NULL)
- g_object_unref (web_view->priv->save_as_proxy);
-
- web_view->priv->save_as_proxy = save_as_proxy;
-
- g_object_notify (G_OBJECT (web_view), "save-as-proxy");
-}
-
-GtkAction *
-e_web_view_gtkhtml_get_action (EWebViewGtkHTML *web_view,
- const gchar *action_name)
-{
- GtkUIManager *ui_manager;
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
- g_return_val_if_fail (action_name != NULL, NULL);
-
- ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
-
- return e_lookup_action (ui_manager, action_name);
-}
-
-GtkActionGroup *
-e_web_view_gtkhtml_get_action_group (EWebViewGtkHTML *web_view,
- const gchar *group_name)
-{
- GtkUIManager *ui_manager;
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
-
- return e_lookup_action_group (ui_manager, group_name);
-}
-
-gchar *
-e_web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkHTML *frame)
-{
- EWebViewGtkHTMLClass *class;
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- if (frame == NULL)
- frame = GTK_HTML (web_view);
-
- class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
- g_return_val_if_fail (class->extract_uri != NULL, NULL);
-
- return class->extract_uri (web_view, event, frame);
-}
-
-void
-e_web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0);
-}
-
-void
-e_web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0);
-}
-
-gboolean
-e_web_view_gtkhtml_is_selection_active (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_command (GTK_HTML (web_view), "is-selection-active");
-}
-
-void
-e_web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0);
-}
-
-gboolean
-e_web_view_gtkhtml_scroll_forward (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_command (GTK_HTML (web_view), "scroll-forward");
-}
-
-gboolean
-e_web_view_gtkhtml_scroll_backward (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
-
- return gtk_html_command (GTK_HTML (web_view), "scroll-backward");
-}
-
-void
-e_web_view_gtkhtml_select_all (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_command (GTK_HTML (web_view), "select-all");
-}
-
-void
-e_web_view_gtkhtml_unselect_all (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_command (GTK_HTML (web_view), "unselect-all");
-}
-
-void
-e_web_view_gtkhtml_zoom_100 (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_command (GTK_HTML (web_view), "zoom-reset");
-}
-
-void
-e_web_view_gtkhtml_zoom_in (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_command (GTK_HTML (web_view), "zoom-in");
-}
-
-void
-e_web_view_gtkhtml_zoom_out (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- gtk_html_command (GTK_HTML (web_view), "zoom-out");
-}
-
-GtkUIManager *
-e_web_view_gtkhtml_get_ui_manager (EWebViewGtkHTML *web_view)
-{
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- return web_view->priv->ui_manager;
-}
-
-GtkWidget *
-e_web_view_gtkhtml_get_popup_menu (EWebViewGtkHTML *web_view)
-{
- GtkUIManager *ui_manager;
- GtkWidget *menu;
-
- g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
-
- ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
- menu = gtk_ui_manager_get_widget (ui_manager, "/context");
- g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
-
- return menu;
-}
-
-void
-e_web_view_gtkhtml_show_popup_menu (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkMenuPositionFunc func,
- gpointer user_data)
-{
- GtkWidget *menu;
-
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- e_web_view_gtkhtml_update_actions (web_view);
-
- menu = e_web_view_gtkhtml_get_popup_menu (web_view);
-
- if (event != NULL)
- gtk_menu_popup (
- GTK_MENU (menu), NULL, NULL, func,
- user_data, event->button, event->time);
- else
- gtk_menu_popup (
- GTK_MENU (menu), NULL, NULL, func,
- user_data, 0, gtk_get_current_event_time ());
-}
-
-void
-e_web_view_gtkhtml_status_message (EWebViewGtkHTML *web_view,
- const gchar *status_message)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
-}
-
-void
-e_web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[STOP_LOADING], 0);
-}
-
-void
-e_web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
-{
- g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
-
- g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
-}
diff --git a/e-util/e-web-view-gtkhtml.h b/e-util/e-web-view-gtkhtml.h
deleted file mode 100644
index 4ad08e9cf6..0000000000
--- a/e-util/e-web-view-gtkhtml.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * e-web-view-gtkhtml.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/>.
- *
- */
-
-/* This is intended to serve as a common base class for all HTML viewing
- * needs in Evolution. Currently based on GtkHTML, the idea is to wrap
- * the GtkHTML API enough that we no longer have to make direct calls to
- * it. This should help smooth the transition to WebKit/GTK+.
- *
- * This class handles basic tasks like mouse hovers over links, clicked
- * links, and servicing URI requests asynchronously via GIO. */
-
-#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
-#error "Only <e-util/e-util.h> should be included directly."
-#endif
-
-#ifndef E_WEB_VIEW_GTKHTML_H
-#define E_WEB_VIEW_GTKHTML_H
-
-#include <gtkhtml/gtkhtml.h>
-
-/* Standard GObject macros */
-#define E_TYPE_WEB_VIEW_GTKHTML \
- (e_web_view_gtkhtml_get_type ())
-#define E_WEB_VIEW_GTKHTML(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTML))
-#define E_WEB_VIEW_GTKHTML_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLClass))
-#define E_IS_WEB_VIEW_GTKHTML(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_WEB_VIEW_GTKHTML))
-#define E_IS_WEB_VIEW_GTKHTML_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_WEB_VIEW_GTKHTML))
-#define E_WEB_VIEW_GTKHTML_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EWebViewGtkHTML EWebViewGtkHTML;
-typedef struct _EWebViewGtkHTMLClass EWebViewGtkHTMLClass;
-typedef struct _EWebViewGtkHTMLPrivate EWebViewGtkHTMLPrivate;
-
-struct _EWebViewGtkHTML {
- GtkHTML parent;
- EWebViewGtkHTMLPrivate *priv;
-};
-
-struct _EWebViewGtkHTMLClass {
- GtkHTMLClass parent_class;
-
- /* Methods */
- gchar * (*extract_uri) (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkHTML *frame);
- void (*hovering_over_link) (EWebViewGtkHTML *web_view,
- const gchar *title,
- const gchar *uri);
- void (*link_clicked) (EWebViewGtkHTML *web_view,
- const gchar *uri);
- void (*load_string) (EWebViewGtkHTML *web_view,
- const gchar *load_string);
-
- /* Signals */
- void (*copy_clipboard) (EWebViewGtkHTML *web_view);
- void (*cut_clipboard) (EWebViewGtkHTML *web_view);
- void (*paste_clipboard) (EWebViewGtkHTML *web_view);
- gboolean (*popup_event) (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- const gchar *uri);
- void (*status_message) (EWebViewGtkHTML *web_view,
- const gchar *status_message);
- void (*stop_loading) (EWebViewGtkHTML *web_view);
- void (*update_actions) (EWebViewGtkHTML *web_view);
- gboolean (*process_mailto) (EWebViewGtkHTML *web_view,
- const gchar *mailto_uri);
-};
-
-GType e_web_view_gtkhtml_get_type (void) G_GNUC_CONST;
-GtkWidget * e_web_view_gtkhtml_new (void);
-void e_web_view_gtkhtml_clear (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
- const gchar *string);
-gboolean e_web_view_gtkhtml_get_animate (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_animate (EWebViewGtkHTML *web_view,
- gboolean animate);
-gboolean e_web_view_gtkhtml_get_caret_mode
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_caret_mode
- (EWebViewGtkHTML *web_view,
- gboolean caret_mode);
-GtkTargetList * e_web_view_gtkhtml_get_copy_target_list
- (EWebViewGtkHTML *web_view);
-gboolean e_web_view_gtkhtml_get_disable_printing
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_disable_printing
- (EWebViewGtkHTML *web_view,
- gboolean disable_printing);
-gboolean e_web_view_gtkhtml_get_disable_save_to_disk
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_disable_save_to_disk
- (EWebViewGtkHTML *web_view,
- gboolean disable_save_to_disk);
-gboolean e_web_view_gtkhtml_get_editable (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_editable (EWebViewGtkHTML *web_view,
- gboolean editable);
-gboolean e_web_view_gtkhtml_get_inline_spelling
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_inline_spelling
- (EWebViewGtkHTML *web_view,
- gboolean inline_spelling);
-gboolean e_web_view_gtkhtml_get_magic_links
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_magic_links
- (EWebViewGtkHTML *web_view,
- gboolean magic_links);
-gboolean e_web_view_gtkhtml_get_magic_smileys
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_magic_smileys
- (EWebViewGtkHTML *web_view,
- gboolean magic_smileys);
-const gchar * e_web_view_gtkhtml_get_selected_uri
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_selected_uri
- (EWebViewGtkHTML *web_view,
- const gchar *selected_uri);
-GdkPixbufAnimation *
- e_web_view_gtkhtml_get_cursor_image
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_cursor_image
- (EWebViewGtkHTML *web_view,
- GdkPixbufAnimation *animation);
-GtkAction * e_web_view_gtkhtml_get_open_proxy
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_open_proxy
- (EWebViewGtkHTML *web_view,
- GtkAction *open_proxy);
-GtkTargetList * e_web_view_gtkhtml_get_paste_target_list
- (EWebViewGtkHTML *web_view);
-GtkAction * e_web_view_gtkhtml_get_print_proxy
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_print_proxy
- (EWebViewGtkHTML *web_view,
- GtkAction *print_proxy);
-GtkAction * e_web_view_gtkhtml_get_save_as_proxy
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_set_save_as_proxy
- (EWebViewGtkHTML *web_view,
- GtkAction *save_as_proxy);
-GtkAction * e_web_view_gtkhtml_get_action (EWebViewGtkHTML *web_view,
- const gchar *action_name);
-GtkActionGroup *e_web_view_gtkhtml_get_action_group
- (EWebViewGtkHTML *web_view,
- const gchar *group_name);
-gchar * e_web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkHTML *frame);
-void e_web_view_gtkhtml_copy_clipboard
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_cut_clipboard
- (EWebViewGtkHTML *web_view);
-gboolean e_web_view_gtkhtml_is_selection_active
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_paste_clipboard
- (EWebViewGtkHTML *web_view);
-gboolean e_web_view_gtkhtml_scroll_forward
- (EWebViewGtkHTML *web_view);
-gboolean e_web_view_gtkhtml_scroll_backward
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_select_all (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_unselect_all (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_zoom_100 (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_zoom_in (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_zoom_out (EWebViewGtkHTML *web_view);
-GtkUIManager * e_web_view_gtkhtml_get_ui_manager
- (EWebViewGtkHTML *web_view);
-GtkWidget * e_web_view_gtkhtml_get_popup_menu
- (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_show_popup_menu
- (EWebViewGtkHTML *web_view,
- GdkEventButton *event,
- GtkMenuPositionFunc func,
- gpointer user_data);
-void e_web_view_gtkhtml_status_message
- (EWebViewGtkHTML *web_view,
- const gchar *status_message);
-void e_web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view);
-void e_web_view_gtkhtml_update_actions
- (EWebViewGtkHTML *web_view);
-
-G_END_DECLS
-
-#endif /* E_WEB_VIEW_GTKHTML_H */
diff --git a/e-util/e-web-view.c b/e-util/e-web-view.c
index afb3cc9fef..0b83d81a34 100644
--- a/e-util/e-web-view.c
+++ b/e-util/e-web-view.c
@@ -359,8 +359,6 @@ static void
web_view_init_web_settings (WebKitWebView *web_view)
{
WebKitWebSettings *web_settings;
- GObjectClass *class;
- GParamSpec *pspec;
web_settings = webkit_web_settings_new ();
@@ -373,19 +371,9 @@ web_view_init_web_settings (WebKitWebView *web_view)
"enable-offline-web-application-cache", FALSE,
"enable-site-specific-quirks", TRUE,
"enable-scripts", FALSE,
+ "respect-image-orientation", TRUE,
NULL);
- /* This property was introduced in WebKitGTK 2.0,
- * so check for it and enable it if it's present. */
- class = G_OBJECT_GET_CLASS (web_settings);
- pspec = g_object_class_find_property (
- class, "respect-image-orientation");
- if (pspec != NULL) {
- g_object_set (
- G_OBJECT (web_settings),
- pspec->name, TRUE, NULL);
- }
-
g_object_bind_property (
web_settings, "enable-caret-browsing",
web_view, "caret-mode",
@@ -3377,9 +3365,9 @@ e_web_view_install_request_handler (EWebView *web_view,
soup_session_add_feature_by_type (session, handler_type);
}
-static void
-create_and_add_css_style_sheet (WebKitDOMDocument *document,
- const gchar *style_sheet_id)
+void
+e_web_view_create_and_add_css_style_sheet (WebKitDOMDocument *document,
+ const gchar *style_sheet_id)
{
WebKitDOMElement *style_element;
@@ -3388,15 +3376,9 @@ create_and_add_css_style_sheet (WebKitDOMDocument *document,
if (!style_element) {
/* Create new <style> element */
style_element = webkit_dom_document_create_element (document, "style", NULL);
-#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */
webkit_dom_element_set_id (
WEBKIT_DOM_ELEMENT (style_element),
style_sheet_id);
-#else
- webkit_dom_html_element_set_id (
- WEBKIT_DOM_HTML_ELEMENT (style_element),
- style_sheet_id);
-#endif
webkit_dom_html_style_element_set_media (
WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element),
"screen");
@@ -3427,7 +3409,7 @@ add_css_rule_into_style_sheet (WebKitDOMDocument *document,
style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
if (!style_element) {
- create_and_add_css_style_sheet (document, style_sheet_id);
+ e_web_view_create_and_add_css_style_sheet (document, style_sheet_id);
style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
}
@@ -3551,3 +3533,125 @@ e_web_view_add_css_rule_into_style_sheet (EWebView *view,
selector,
style);
}
+
+gboolean
+element_has_id (WebKitDOMElement *element,
+ const gchar* id)
+{
+ gchar *element_id;
+
+ if (!element)
+ return FALSE;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element))
+ return FALSE;
+
+ element_id = webkit_dom_element_get_id (element);
+
+ if (g_ascii_strcasecmp (element_id, id) != 0) {
+ g_free (element_id);
+ return FALSE;
+ }
+ g_free (element_id);
+
+ return TRUE;
+}
+
+gboolean
+element_has_tag (WebKitDOMElement *element,
+ const gchar* tag)
+{
+ gchar *element_tag;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element))
+ return FALSE;
+
+ element_tag = webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element));
+
+ if (g_ascii_strcasecmp (element_tag, tag) != 0) {
+ g_free (element_tag);
+ return FALSE;
+ }
+ g_free (element_tag);
+
+ return TRUE;
+}
+
+gboolean
+element_has_class (WebKitDOMElement *element,
+ const gchar* class)
+{
+ gchar *element_class;
+
+ if (!element)
+ return FALSE;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element))
+ return FALSE;
+
+ element_class = webkit_dom_element_get_class_name (element);
+
+ if (g_strstr_len (element_class, -1, class)) {
+ g_free (element_class);
+ return TRUE;
+ }
+ g_free (element_class);
+
+ return FALSE;
+}
+
+void
+element_add_class (WebKitDOMElement *element,
+ const gchar* class)
+{
+ gchar *element_class;
+ gchar *new_class;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element))
+ return;
+
+ if (element_has_class (element, class))
+ return;
+
+ element_class = webkit_dom_element_get_class_name (element);
+
+ if (g_strcmp0 (element_class, "") == 0)
+ new_class = g_strdup (class);
+ else
+ new_class = g_strconcat (element_class, " ", class, NULL);
+
+ webkit_dom_element_set_class_name (element, new_class);
+
+ g_free (element_class);
+ g_free (new_class);
+}
+
+void
+element_remove_class (WebKitDOMElement *element,
+ const gchar* class)
+{
+ gchar *element_class;
+ GString *result;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (element))
+ return;
+
+ if (!element_has_class (element, class))
+ return;
+
+ element_class = webkit_dom_element_get_class_name (element);
+
+ if (g_strcmp0 (element_class, class) == 0) {
+ webkit_dom_element_remove_attribute (element, "class");
+ g_free (element_class);
+ return;
+ }
+
+ result = e_str_replace_string (element_class, class, "");
+ if (result) {
+ webkit_dom_element_set_class_name (element, result->str);
+ g_string_free (result, TRUE);
+ }
+
+ g_free (element_class);
+}
diff --git a/e-util/e-web-view.h b/e-util/e-web-view.h
index 62bd045f8a..cbfd2cefad 100644
--- a/e-util/e-web-view.h
+++ b/e-util/e-web-view.h
@@ -197,11 +197,24 @@ GInputStream * e_web_view_request_finish (EWebView *web_view,
void e_web_view_install_request_handler
(EWebView *web_view,
GType handler_type);
+void e_web_view_create_and_add_css_style_sheet
+ (WebKitDOMDocument* document,
+ const gchar *style_sheet_id);
void e_web_view_add_css_rule_into_style_sheet
(EWebView *web_view,
const gchar *style_sheet_id,
const gchar *selector,
const gchar *style);
+gboolean element_has_id (WebKitDOMElement *element,
+ const gchar* id);
+gboolean element_has_tag (WebKitDOMElement *element,
+ const gchar* tag);
+gboolean element_has_class (WebKitDOMElement *element,
+ const gchar* class);
+void element_add_class (WebKitDOMElement *element,
+ const gchar* class);
+void element_remove_class (WebKitDOMElement *element,
+ const gchar* class);
G_END_DECLS
#endif /* E_WEB_VIEW_H */
diff --git a/e-util/test-html-editor.c b/e-util/test-html-editor.c
new file mode 100644
index 0000000000..488eb44bf1
--- /dev/null
+++ b/e-util/test-html-editor.c
@@ -0,0 +1,497 @@
+/*
+ * e-html-editor-test.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <e-util/e-util.h>
+
+#include <glib/gi18n-lib.h>
+
+static const gchar *file_ui =
+"<ui>\n"
+" <menubar name='main-menu'>\n"
+" <menu action='file-menu'>\n"
+" <menuitem action='save'/>\n"
+" <menuitem action='save-as'/>\n"
+" <separator/>\n"
+" <menuitem action='print-preview'/>\n"
+" <menuitem action='print'/>\n"
+" <separator/>\n"
+" <menuitem action='disable-editor'/>\n"
+" <separator/>\n"
+" <menuitem action='quit'/>\n"
+" </menu>\n"
+" </menubar>\n"
+"</ui>";
+
+static const gchar *view_ui =
+"<ui>\n"
+" <menubar name='main-menu'>\n"
+" <menu action='view-menu'>\n"
+" <menuitem action='view-html-output'/>\n"
+" <menuitem action='view-html-source'/>\n"
+" <menuitem action='view-plain-source'/>\n"
+" <menuitem action='view-inspector'/>\n"
+" </menu>\n"
+" </menubar>\n"
+"</ui>";
+
+static void
+handle_error (GError **error)
+{
+ if (*error != NULL) {
+ g_warning ("%s", (*error)->message);
+ g_clear_error (error);
+ }
+}
+
+static GtkPrintOperationResult
+print (EHTMLEditor *editor,
+ GtkPrintOperationAction action)
+{
+ WebKitWebFrame *frame;
+ GtkPrintOperation *operation;
+ GtkPrintOperationResult result;
+ GError *error = NULL;
+
+ operation = gtk_print_operation_new ();
+
+ frame = webkit_web_view_get_main_frame (
+ WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+ result = webkit_web_frame_print_full (frame, operation, action, NULL);
+
+ g_object_unref (operation);
+ handle_error (&error);
+
+ return result;
+}
+
+static gint
+save_dialog (EHTMLEditor *editor)
+{
+ GtkWidget *dialog;
+ const gchar *filename;
+ gint response;
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Save As"),
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (
+ GTK_FILE_CHOOSER (dialog), TRUE);
+
+ filename = e_html_editor_get_filename (editor);
+
+ if (filename != NULL)
+ gtk_file_chooser_set_filename (
+ GTK_FILE_CHOOSER (dialog), filename);
+ else
+ gtk_file_chooser_set_current_name (
+ GTK_FILE_CHOOSER (dialog), _("Untitled document"));
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ gchar *new_filename;
+
+ new_filename = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog));
+ e_html_editor_set_filename (editor, new_filename);
+ g_free (new_filename);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+static void
+view_source_dialog (EHTMLEditor *editor,
+ const gchar *title,
+ gboolean plain_text,
+ gboolean show_source)
+{
+ GtkWidget *dialog;
+ GtkWidget *content;
+ GtkWidget *content_area;
+ GtkWidget *scrolled_window;
+ gchar * html;
+
+ dialog = gtk_dialog_new_with_buttons (
+ title,
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+ gtk_box_pack_start (
+ GTK_BOX (content_area),
+ scrolled_window, TRUE, TRUE, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 6);
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300);
+
+ if (plain_text) {
+ html = e_html_editor_view_get_text_plain (
+ e_html_editor_get_view (editor));
+ } else {
+ html = e_html_editor_view_get_text_html (
+ e_html_editor_get_view (editor));
+ }
+
+ if (show_source || plain_text) {
+ GtkTextBuffer *buffer;
+
+ content = gtk_text_view_new ();
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (content));
+ gtk_text_buffer_set_text (buffer, html, -1);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE);
+ } else {
+ content = webkit_web_view_new ();
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (content), html, NULL, NULL, NULL);
+ }
+ g_free (html);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), content);
+ gtk_widget_show_all (scrolled_window);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+action_print_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ print (editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
+}
+
+static void
+action_print_preview_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ print (editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
+}
+
+static void
+action_quit_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ gtk_main_quit ();
+}
+
+static void
+action_save_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ const gchar *filename;
+ gboolean as_html;
+ GError *error = NULL;
+
+ if (e_html_editor_get_filename (editor) == NULL)
+ if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
+ return;
+
+ filename = e_html_editor_get_filename (editor);
+ as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+
+ e_html_editor_save (editor, filename, as_html, &error);
+ handle_error (&error);
+}
+
+static void
+action_save_as_cb (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ const gchar *filename;
+ gboolean as_html;
+ GError *error = NULL;
+
+ if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
+ return;
+
+ filename = e_html_editor_get_filename (editor);
+ as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+
+ e_html_editor_save (editor, filename, as_html, &error);
+ handle_error (&error);
+}
+
+static void
+action_toggle_editor (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ EHTMLEditorView *view;
+
+ view = e_html_editor_get_view (editor);
+ webkit_web_view_set_editable (
+ WEBKIT_WEB_VIEW (view),
+ ! webkit_web_view_get_editable (WEBKIT_WEB_VIEW (view)));
+}
+
+static void
+action_view_html_output (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ view_source_dialog (editor, _("HTML Output"), FALSE, FALSE);
+}
+
+static void
+action_view_html_source (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ view_source_dialog (editor, _("HTML Source"), FALSE, TRUE);
+}
+
+static void
+action_view_plain_source (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ view_source_dialog (editor, _("Plain Source"), TRUE, FALSE);
+}
+
+static void
+action_view_inspector (GtkAction *action,
+ EHTMLEditor *editor)
+{
+ WebKitWebInspector *inspector;
+ EHTMLEditorView *view;
+
+ view = e_html_editor_get_view (editor);
+ inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
+
+ webkit_web_inspector_show (inspector);
+}
+
+static GtkActionEntry file_entries[] = {
+
+ { "print",
+ GTK_STOCK_PRINT,
+ N_("_Print..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_print_cb) },
+
+ { "print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ N_("Print Pre_view"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_print_preview_cb) },
+
+ { "quit",
+ GTK_STOCK_QUIT,
+ N_("_Quit"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_quit_cb) },
+
+ { "save",
+ GTK_STOCK_SAVE,
+ N_("_Save"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_save_cb) },
+
+ { "save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("Save _As..."),
+ NULL,
+ NULL,
+ G_CALLBACK (action_save_as_cb) },
+
+ { "disable-editor",
+ NULL,
+ N_("Disable/Enable Editor"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_toggle_editor) },
+
+ { "file-menu",
+ NULL,
+ N_("_File"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static GtkActionEntry view_entries[] = {
+
+ { "view-html-output",
+ NULL,
+ N_("HTML _Output"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_view_html_output) },
+
+ { "view-html-source",
+ NULL,
+ N_("_HTML Source"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_view_html_source) },
+
+ { "view-plain-source",
+ NULL,
+ N_("_Plain Source"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_view_plain_source) },
+
+ { "view-inspector",
+ NULL,
+ N_("Inspector"),
+ NULL,
+ NULL,
+ G_CALLBACK (action_view_inspector) },
+
+ { "view-menu",
+ NULL,
+ N_("_View"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static WebKitWebView *
+open_inspector (WebKitWebInspector *inspector,
+ WebKitWebView *webview,
+ gpointer user_data)
+{
+ GtkWidget *window;
+ GtkWidget *inspector_view;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ inspector_view = webkit_web_view_new ();
+
+ gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_view));
+
+ gtk_widget_set_size_request (window, 600, 480);
+ gtk_widget_show (window);
+
+ return WEBKIT_WEB_VIEW (inspector_view);
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ GtkActionGroup *action_group;
+ GtkUIManager *manager;
+ GtkWidget *container;
+ GtkWidget *widget;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+ WebKitWebInspector *inspector;
+
+ GError *error = NULL;
+
+ bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ gtk_init (&argc, &argv);
+
+ editor = g_object_ref_sink (e_html_editor_new ());
+ view = e_html_editor_get_view (editor);
+
+ inspector = webkit_web_view_get_inspector (
+ WEBKIT_WEB_VIEW (view));
+ g_signal_connect (
+ inspector, "inspect-web-view",
+ G_CALLBACK (open_inspector), NULL);
+
+ widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_size_request (widget, 600, 400);
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "destroy",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ container = widget;
+
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_html_editor_get_managed_widget (editor, "/main-menu");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = GTK_WIDGET (editor);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ manager = e_html_editor_get_ui_manager (editor);
+
+ gtk_ui_manager_add_ui_from_string (manager, file_ui, -1, &error);
+ handle_error (&error);
+
+ gtk_ui_manager_add_ui_from_string (manager, view_ui, -1, &error);
+ handle_error (&error);
+
+ action_group = gtk_action_group_new ("file");
+ gtk_action_group_set_translation_domain (
+ action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (
+ action_group, file_entries,
+ G_N_ELEMENTS (file_entries), editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ action_group = gtk_action_group_new ("view");
+ gtk_action_group_set_translation_domain (
+ action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (
+ action_group, view_entries,
+ G_N_ELEMENTS (view_entries), editor);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ gtk_ui_manager_ensure_update (manager);
+
+ g_signal_connect (
+ editor, "destroy",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_main ();
+
+ g_object_unref (editor);
+
+ return 0;
+}
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index dabc083404..7827d79198 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -52,9 +52,9 @@ libevolution_mail_formatter_la_CPPFLAGS = \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBSOUP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
if ENABLE_SMIME
SMIME_EXTENSIONS = e-mail-parser-application-smime.c
@@ -139,9 +139,9 @@ libevolution_mail_formatter_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(LIBSOUP_LIBS) \
- $(SMIME_LIBS)
+ $(SMIME_LIBS) \
+ $(NULL)
BUILT_SOURCES = \
$(ENUM_GENERATED) \
diff --git a/em-format/e-mail-formatter-enums.h b/em-format/e-mail-formatter-enums.h
index 862d66e4fa..e7b1868db3 100644
--- a/em-format/e-mail-formatter-enums.h
+++ b/em-format/e-mail-formatter-enums.h
@@ -61,24 +61,6 @@ typedef enum { /*< flags >*/
} EMailFormatterQuoteFlags;
/**
- * EMailImageLoadingPolicy:
- * @E_MAIL_IMAGE_LOADING_POLICY_NEVER:
- * Never load images from a remote server.
- * @E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES:
- * Only load images from a remote server if the sender is a known contact.
- * @E_MAIL_IMAGE_LOADING_POLICY_ALWAYS:
- * Always load images from a remote server.
- *
- * Policy for loading remote image URLs in email. Allowing images to be
- * loaded from a remote server may have privacy implications.
- **/
-typedef enum {
- E_MAIL_IMAGE_LOADING_POLICY_NEVER,
- E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES,
- E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
-} EMailImageLoadingPolicy;
-
-/**
* EMailParserExtensionFlags:
* @E_MAIL_PARSER_EXTENSION_INLINE:
* Don't parse as attachment.
diff --git a/em-format/e-mail-formatter-quote-attachment.c b/em-format/e-mail-formatter-quote-attachment.c
index 0ec4889b5b..dcc5300e45 100644
--- a/em-format/e-mail-formatter-quote-attachment.c
+++ b/em-format/e-mail-formatter-quote-attachment.c
@@ -96,10 +96,7 @@ emfqe_attachment_format (EMailFormatterExtension *extension,
g_free (html);
g_free (text);
- string =
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- "key=\"orig\" value=\"1\">-->\n"
- "<blockquote type=cite>\n";
+ string = "<blockquote type=cite>\n";
g_output_stream_write_all (
stream, string, strlen (string), NULL, cancellable, NULL);
@@ -107,9 +104,7 @@ emfqe_attachment_format (EMailFormatterExtension *extension,
formatter, context, attachment_view_part,
stream, NULL, cancellable);
- string =
- "</blockquote><!--+GtkHTML:"
- "<DATA class=\"ClueFlow\" clear=\"orig\">-->";
+ string = "</blockquote>";
g_output_stream_write_all (
stream, string, strlen (string), NULL, cancellable, NULL);
diff --git a/em-format/e-mail-formatter-quote-text-html.c b/em-format/e-mail-formatter-quote-text-html.c
index f6a47a9ab2..8c6433c0c0 100644
--- a/em-format/e-mail-formatter-quote-text-html.c
+++ b/em-format/e-mail-formatter-quote-text-html.c
@@ -59,7 +59,7 @@ emqfe_text_html_format (EMailFormatterExtension *extension,
qf_context = (EMailFormatterQuoteContext *) context;
- string = "\n<!-- text/html -->\n";
+ string = "<!-- text/html -->";
g_output_stream_write_all (
stream, string, strlen (string), NULL, cancellable, NULL);
diff --git a/em-format/e-mail-formatter-quote-text-plain.c b/em-format/e-mail-formatter-quote-text-plain.c
index 318762c125..ce97e86307 100644
--- a/em-format/e-mail-formatter-quote-text-plain.c
+++ b/em-format/e-mail-formatter-quote-text-plain.c
@@ -73,8 +73,10 @@ emqfe_text_plain_format (EMailFormatterExtension *extension,
CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+ /* XXX Should we define a separate EMailFormatter property
+ * for using CAMEL_MIME_FILTER_TOHTML_QUOTE_CITATION? */
if (e_mail_formatter_get_mark_citations (formatter))
- text_flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+ text_flags |= CAMEL_MIME_FILTER_TOHTML_QUOTE_CITATION;
/* Check for RFC 2646 flowed text. */
type = camel_mime_part_get_content_type (mime_part);
diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c
index 8eda05fa9b..b402eda29c 100644
--- a/em-format/e-mail-formatter-quote.c
+++ b/em-format/e-mail-formatter-quote.c
@@ -57,7 +57,6 @@ mail_formatter_quote_run (EMailFormatter *formatter,
{
EMailFormatterQuote *qf;
EMailFormatterQuoteContext *qf_context;
- GSettings *settings;
GQueue queue = G_QUEUE_INIT;
GList *head, *link;
const gchar *string;
@@ -74,31 +73,6 @@ mail_formatter_quote_run (EMailFormatter *formatter,
G_SEEKABLE (stream),
0, G_SEEK_SET, NULL, NULL);
- settings = g_settings_new ("org.gnome.evolution.mail");
- if (g_settings_get_boolean (settings, "composer-top-signature")) {
- string = "<br>\n";
- g_output_stream_write_all (
- stream, string, strlen (string),
- NULL, cancellable, NULL);
- }
- g_object_unref (settings);
-
- if (qf->priv->credits != NULL && *qf->priv->credits != '\0') {
- g_output_stream_write_all (
- stream, qf->priv->credits,
- strlen (qf->priv->credits),
- NULL, cancellable, NULL);
- }
-
- if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) {
- string = "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- "key=\"orig\" value=\"1\">-->\n"
- "<blockquote type=cite>\n";
- g_output_stream_write_all (
- stream, string, strlen (string),
- NULL, cancellable, NULL);
- }
-
e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
head = g_queue_peek_head_link (&queue);
@@ -133,9 +107,33 @@ mail_formatter_quote_run (EMailFormatter *formatter,
while (!g_queue_is_empty (&queue))
g_object_unref (g_queue_pop_head (&queue));
+ /* Before we were inserting the BR elements and the credits in front of
+ * the actual HTML code of the message. But this was wrong as when WebKit
+ * was loading the given HTML code that looked like
+ * <br>CREDITS<html>MESSAGE_CODE</html> WebKit parsed it like
+ * <html><br>CREDITS</html><html>MESSAGE_CODE</html>. As no elements are
+ * allowed outside of the HTML root element WebKit wrapped them into
+ * another HTML root element. Afterwards the first root element was
+ * treated as the primary one and all the elements from the second's root
+ * HEAD and BODY elements were moved to the first one.
+ * Thus the HTML that was loaded into composer contained the i.e. META
+ * or STYLE definitions in the body.
+ * So if we want to put something into the message we have to put it into
+ * the special span element and it will be moved to body in EHTMLEditorView */
+ if (qf->priv->credits && *qf->priv->credits) {
+ gchar *credits = g_strdup_printf (
+ "<span class=\"-x-evo-to-body\"><pre>%s</pre></span>", qf->priv->credits);
+ g_output_stream_write_all (
+ stream, credits, strlen (credits),
+ NULL, cancellable, NULL);
+ g_free (credits);
+ }
+
+ /* If we want to cite the message we have to append the special span element
+ * after the message and cite it in EHTMLEditorView because of reasons
+ * mentioned above */
if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) {
- string = "</blockquote><!--+GtkHTML:"
- "<DATA class=\"ClueFlow\" clear=\"orig\">-->";
+ string = "<span class=\"-x-evo-cite-body\"><span>";
g_output_stream_write_all (
stream, string, strlen (string),
NULL, cancellable, NULL);
diff --git a/em-format/e-mail-formatter.c b/em-format/e-mail-formatter.c
index 2efe8ebd8c..a21f51c308 100644
--- a/em-format/e-mail-formatter.c
+++ b/em-format/e-mail-formatter.c
@@ -41,7 +41,7 @@
typedef struct _AsyncContext AsyncContext;
struct _EMailFormatterPrivate {
- EMailImageLoadingPolicy image_loading_policy;
+ EImageLoadingPolicy image_loading_policy;
gboolean show_sender_photo;
gboolean show_real_date;
@@ -705,8 +705,8 @@ e_mail_formatter_class_init (EMailFormatterClass *class)
"image-loading-policy",
"Image Loading Policy",
NULL,
- E_TYPE_MAIL_IMAGE_LOADING_POLICY,
- E_MAIL_IMAGE_LOADING_POLICY_NEVER,
+ E_TYPE_IMAGE_LOADING_POLICY,
+ E_IMAGE_LOADING_POLICY_NEVER,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
@@ -1229,7 +1229,7 @@ e_mail_formatter_update_style (EMailFormatter *formatter,
class->update_style (formatter, state);
}
-EMailImageLoadingPolicy
+EImageLoadingPolicy
e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
{
g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
@@ -1239,7 +1239,7 @@ e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
void
e_mail_formatter_set_image_loading_policy (EMailFormatter *formatter,
- EMailImageLoadingPolicy policy)
+ EImageLoadingPolicy policy)
{
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h
index bbd61f22dc..a05755cb64 100644
--- a/em-format/e-mail-formatter.h
+++ b/em-format/e-mail-formatter.h
@@ -143,12 +143,12 @@ void e_mail_formatter_set_color (EMailFormatter *formatter,
void e_mail_formatter_update_style (EMailFormatter *formatter,
GtkStateFlags state);
-EMailImageLoadingPolicy
+EImageLoadingPolicy
e_mail_formatter_get_image_loading_policy
(EMailFormatter *formatter);
void e_mail_formatter_set_image_loading_policy
(EMailFormatter *formatter,
- EMailImageLoadingPolicy policy);
+ EImageLoadingPolicy policy);
gboolean e_mail_formatter_get_mark_citations
(EMailFormatter *formatter);
diff --git a/libemail-engine/Makefile.am b/libemail-engine/Makefile.am
index 3064d829fb..4dd1bd4ae5 100644
--- a/libemail-engine/Makefile.am
+++ b/libemail-engine/Makefile.am
@@ -24,7 +24,6 @@ libemail_engine_la_CPPFLAGS = \
-DLIBEMAIL_ENGINE_COMPILATION \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -81,7 +80,6 @@ libemail_engine_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
libemail_engine_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/mail/Makefile.am b/mail/Makefile.am
index d40922e605..d0b7220c89 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -46,10 +46,10 @@ libevolution_mail_la_CPPFLAGS = \
$(GNOME_PLATFORM_CFLAGS) \
$(CERT_UI_CFLAGS) \
$(CANBERRA_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBCRYPTUI_CFLAGS) \
$(LIBSOUP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
mailinclude_HEADERS = \
e-http-request.h \
@@ -225,7 +225,6 @@ libevolution_mail_la_LIBADD = \
$(GNOME_PLATFORM_LIBS) \
$(CERT_UI_LIBS) \
$(CANBERRA_LIBS) \
- $(GTKHTML_LIBS) \
$(SMIME_LIBS) \
$(LIBCRYPTUI_LIBS) \
$(LIBSOUP_LIBS) \
diff --git a/mail/e-http-request.c b/mail/e-http-request.c
index 534e30bff6..39db7a1b84 100644
--- a/mail/e-http-request.c
+++ b/mail/e-http-request.c
@@ -165,17 +165,16 @@ handle_http_request (GSimpleAsyncResult *res,
SoupRequest *soup_request;
SoupSession *soup_session;
gchar *evo_uri, *uri;
- gchar *mail_uri;
+ gchar *mail_uri = NULL;
GInputStream *stream;
gboolean force_load_images = FALSE;
- EMailImageLoadingPolicy image_policy;
+ EImageLoadingPolicy image_policy;
gchar *uri_md5;
EShell *shell;
GSettings *settings;
- const gchar *user_cache_dir;
+ const gchar *user_cache_dir, *soup_query;
CamelDataCache *cache;
GIOStream *cache_stream;
- GHashTable *query;
gint uri_len;
if (g_cancellable_is_cancelled (cancellable))
@@ -185,24 +184,27 @@ handle_http_request (GSimpleAsyncResult *res,
soup_request = SOUP_REQUEST (source_object);
soup_session = soup_request_get_session (soup_request);
+ soup_uri = soup_request_get_uri (soup_request);
/* Remove the __evo-mail query */
- soup_uri = soup_request_get_uri (soup_request);
- g_return_if_fail (soup_uri_get_query (soup_uri) != NULL);
+ soup_query = soup_uri_get_query (soup_uri);
+ if (soup_query) {
+ GHashTable *query;
- query = soup_form_decode (soup_uri_get_query (soup_uri));
- mail_uri = g_hash_table_lookup (query, "__evo-mail");
- if (mail_uri)
- mail_uri = g_strdup (mail_uri);
+ query = soup_form_decode (soup_uri_get_query (soup_uri));
+ mail_uri = g_hash_table_lookup (query, "__evo-mail");
+ if (mail_uri)
+ mail_uri = g_strdup (mail_uri);
- g_hash_table_remove (query, "__evo-mail");
+ g_hash_table_remove (query, "__evo-mail");
- /* Remove __evo-load-images if present (and in such case set
- * force_load_images to TRUE) */
- force_load_images = g_hash_table_remove (query, "__evo-load-images");
+ /* Remove __evo-load-images if present (and in such case set
+ * force_load_images to TRUE) */
+ force_load_images = g_hash_table_remove (query, "__evo-load-images");
- soup_uri_set_query_from_form (soup_uri, query);
- g_hash_table_unref (query);
+ soup_uri_set_query_from_form (soup_uri, query);
+ g_hash_table_unref (query);
+ }
evo_uri = soup_uri_to_string (soup_uri, FALSE);
@@ -305,7 +307,7 @@ handle_http_request (GSimpleAsyncResult *res,
/* Item not found in cache, but image loading policy allows us to fetch
* it from the interwebs */
if (!force_load_images && mail_uri != NULL &&
- (image_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES)) {
+ (image_policy == E_IMAGE_LOADING_POLICY_SOMETIMES)) {
CamelObjectBag *registry;
gchar *decoded_uri;
EMailPartList *part_list;
@@ -350,7 +352,7 @@ handle_http_request (GSimpleAsyncResult *res,
g_free (decoded_uri);
}
- if ((image_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) ||
+ if ((image_policy == E_IMAGE_LOADING_POLICY_ALWAYS) ||
force_load_images) {
SoupSession *temp_session;
@@ -488,27 +490,25 @@ http_request_send_async (SoupRequest *request,
gpointer user_data)
{
GSimpleAsyncResult *simple;
- SoupURI *uri;
- const gchar *enc;
- GHashTable *query;
-
- uri = soup_request_get_uri (request);
- g_return_if_fail (soup_uri_get_query (uri) != NULL);
-
- query = soup_form_decode (soup_uri_get_query (uri));
d ({
- gchar *uri_str = soup_uri_to_string (uri, FALSE);
- printf ("received request for %s\n", uri_str);
- g_free (uri_str);
- });
+ const gchar *soup_query;
+ SoupURI *uri;
- enc = g_hash_table_lookup (query, "__evo-mail");
+ uri = soup_request_get_uri (request);
+ soup_query = soup_uri_get_query (uri);
- if (enc == NULL || *enc == '\0') {
- g_hash_table_destroy (query);
- return;
- }
+ if (soup_query) {
+ gchar *uri_str;
+ GHashTable *query;
+
+ query = soup_form_decode (soup_uri_get_query (uri));
+ uri_str = soup_uri_to_string (uri, FALSE);
+ printf ("received request for %s\n", uri_str);
+ g_free (uri_str);
+ g_hash_table_destroy (query);
+ }
+ });
simple = g_simple_async_result_new (
G_OBJECT (request), callback,
@@ -521,8 +521,6 @@ http_request_send_async (SoupRequest *request,
G_PRIORITY_DEFAULT, cancellable);
g_object_unref (simple);
-
- g_hash_table_destroy (query);
}
static GInputStream *
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 61751dfbee..7741a40bee 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -142,11 +142,11 @@ formatter_image_loading_policy_changed_cb (GObject *object,
{
EMailDisplay *display = user_data;
EMailFormatter *formatter = E_MAIL_FORMATTER (object);
- EMailImageLoadingPolicy policy;
+ EImageLoadingPolicy policy;
policy = e_mail_formatter_get_image_loading_policy (formatter);
- if (policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
+ if (policy == E_IMAGE_LOADING_POLICY_ALWAYS)
e_mail_display_load_images (display);
else
e_mail_display_reload (display);
@@ -1294,7 +1294,7 @@ mail_display_redirect_uri (EWebView *web_view,
SoupURI *soup_uri;
GHashTable *query;
gboolean image_exists;
- EMailImageLoadingPolicy image_policy;
+ EImageLoadingPolicy image_policy;
/* Check Evolution's cache */
image_exists = mail_display_image_exists_in_cache (uri);
@@ -1305,7 +1305,7 @@ mail_display_redirect_uri (EWebView *web_view,
image_policy = e_mail_formatter_get_image_loading_policy (
display->priv->formatter);
if (!image_exists && !display->priv->force_image_load &&
- (image_policy == E_MAIL_IMAGE_LOADING_POLICY_NEVER)) {
+ (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
return g_strdup ("about:blank");
}
@@ -1352,39 +1352,46 @@ chainup:
redirect_uri (web_view, uri);
}
-static gchar *
-mail_display_suggest_filename (EWebView *web_view,
- const gchar *uri)
+static CamelMimePart *
+camel_mime_part_from_cid (EMailDisplay *display,
+ const gchar *uri)
{
- if (g_str_has_prefix (uri, "cid:")) {
- EMailDisplay *display;
- EMailPartList *part_list;
- CamelMimeMessage *message;
- CamelMimePart *mime_part;
- const gchar *filename;
+ EMailPartList *part_list;
+ CamelMimeMessage *message;
+ CamelMimePart *mime_part;
- /* Note, this assumes the URI comes
- * from the currently loaded message. */
+ if (!g_str_has_prefix (uri, "cid:"))
+ return NULL;
- display = E_MAIL_DISPLAY (web_view);
+ part_list = e_mail_display_get_part_list (display);
+ if (!part_list)
+ return NULL;
- part_list = e_mail_display_get_part_list (display);
- if (part_list == NULL)
- return NULL;
+ message = e_mail_part_list_get_message (part_list);
+ if (!message)
+ return NULL;
- message = e_mail_part_list_get_message (part_list);
- if (message == NULL)
- return NULL;
+ mime_part = camel_mime_message_get_part_by_content_id (
+ message, uri + 4);
- mime_part = camel_mime_message_get_part_by_content_id (
- message, uri + 4);
- if (mime_part == NULL)
- return NULL;
+ return mime_part;
+}
- filename = camel_mime_part_get_filename (mime_part);
+static gchar *
+mail_display_suggest_filename (EWebView *web_view,
+ const gchar *uri)
+{
+ EMailDisplay *display;
+ CamelMimePart *mime_part;
- return g_strdup (filename);
- }
+ /* Note, this assumes the URI comes
+ * from the currently loaded message. */
+ display = E_MAIL_DISPLAY (web_view);
+
+ mime_part = camel_mime_part_from_cid (display, uri);
+
+ if (mime_part)
+ return g_strdup (camel_mime_part_get_filename (mime_part));
/* Chain up to parent's suggest_filename() method. */
return E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
@@ -1446,6 +1453,68 @@ e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display,
}
static void
+mail_display_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EMailDisplay *display)
+{
+ CamelDataWrapper *dw;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ gchar *src, *base64_encoded, *mime_type, *uri;
+ const gchar *filename;
+ const guchar *data_from_webkit;
+ gint length;
+ GByteArray *byte_array;
+
+ data_from_webkit = gtk_selection_data_get_data (data);
+ length = gtk_selection_data_get_length (data);
+
+ uri = g_strndup ((const gchar *) data_from_webkit, length);
+
+ mime_part = camel_mime_part_from_cid (display, uri);
+
+ if (!mime_part)
+ goto out;
+
+ stream = camel_stream_mem_new ();
+ dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+ g_return_if_fail (dw);
+
+ mime_type = camel_data_wrapper_get_mime_type (dw);
+ camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+ camel_stream_close (stream, NULL, NULL);
+
+ byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+ if (!byte_array->data) {
+ g_object_unref (stream);
+ goto out;
+ }
+
+ base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+ filename = camel_mime_part_get_filename (mime_part);
+ /* Insert filename before base64 data */
+ src = g_strconcat (filename, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+ gtk_selection_data_set (
+ data,
+ gtk_selection_data_get_data_type (data),
+ gtk_selection_data_get_format (data),
+ (const guchar *) src, strlen (src));
+
+ g_free (src);
+ g_free (base64_encoded);
+ g_free (mime_type);
+ g_object_unref (stream);
+ out:
+ g_free (uri);
+}
+
+static void
e_mail_display_class_init (EMailDisplayClass *class)
{
GObjectClass *object_class;
@@ -1575,6 +1644,9 @@ e_mail_display_init (EMailDisplay *display)
g_signal_connect (
display, "document-load-finished",
G_CALLBACK (initialize_web_view_colors), NULL);
+ g_signal_connect_after (
+ display, "drag-data-get",
+ G_CALLBACK (mail_display_drag_data_get), display);
display->priv->settings = g_settings_new ("org.gnome.evolution.mail");
g_signal_connect_swapped (
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index 07ac1928f6..f1a6126907 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -28,7 +28,6 @@
#include <glib/gi18n.h>
#include <libxml/tree.h>
-#include <gtkhtml/gtkhtml.h>
#include <camel/camel.h>
#include <shell/e-shell-utils.h>
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index cbc595a042..46c4cf92e3 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -2464,7 +2464,7 @@ mail_reader_key_press_event_cb (EMailReader *reader,
if (frame != NULL) {
dom = webkit_web_frame_get_dom_document (frame);
/* intentionally used "static_cast" */
- element = webkit_dom_html_document_get_active_element ((WebKitDOMHTMLDocument *) dom);
+ element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom));
if (element != NULL)
name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 22cc594273..afa7e28c18 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -296,9 +296,11 @@ composer_presend_check_recipients (EMsgComposer *composer,
/* I'm sensing a lack of love, er, I mean recipients. */
if (num == 0 && num_post == 0) {
- e_alert_submit (
- E_ALERT_SINK (composer),
- "mail:send-no-recipients", NULL);
+ EHTMLEditor *editor;
+
+ editor = e_msg_composer_get_editor (composer);
+ e_alert_submit (E_ALERT_SINK (editor), "mail:send-no-recipients", NULL);
+
goto finished;
}
@@ -443,6 +445,8 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
EMailSession *session)
{
EDestination **recipients;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
EComposerHeaderTable *table;
GSettings *settings;
gboolean check_passed = TRUE;
@@ -453,9 +457,12 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
settings = g_settings_new ("org.gnome.evolution.mail");
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+ html_mode = e_html_editor_view_get_html_mode (view);
+
table = e_msg_composer_get_header_table (composer);
recipients = e_composer_header_table_get_destinations (table);
- html_mode = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer));
send_html = g_settings_get_boolean (settings, "composer-send-html");
confirm_html = g_settings_get_boolean (settings, "prompt-on-unwanted-html");
@@ -585,8 +592,13 @@ exit:
g_clear_error (&local_error);
if (set_changed) {
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+
+ editor = e_msg_composer_get_editor (async_context->composer);
+ view = e_html_editor_get_view (editor);
+ e_html_editor_view_set_changed (view, TRUE);
+
gtk_window_present (GTK_WINDOW (async_context->composer));
}
@@ -638,14 +650,15 @@ em_utils_composer_send_cb (EMsgComposer *composer,
static void
composer_set_no_change (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
g_return_if_fail (composer != NULL);
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
- gtkhtml_editor_drop_undo (editor);
- gtkhtml_editor_set_changed (editor, FALSE);
+ e_html_editor_view_set_changed (view, FALSE);
}
/* delete original messages from Outbox folder */
@@ -690,8 +703,15 @@ composer_save_to_drafts_complete (GObject *source_object,
{
EActivity *activity;
AsyncContext *async_context;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GError *local_error = NULL;
+ async_context = (AsyncContext *) user_data;
+
+ editor = e_msg_composer_get_editor (async_context->composer);
+ view = e_html_editor_get_view (editor);
+
/* We don't really care if this failed. If something other than
* cancellation happened, emit a runtime warning so the error is
* not completely lost. */
@@ -704,13 +724,11 @@ composer_save_to_drafts_complete (GObject *source_object,
E_MAIL_SESSION (source_object), result, &local_error);
if (e_activity_handle_cancellation (activity, local_error)) {
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
g_error_free (local_error);
} else if (local_error != NULL) {
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
g_warning ("%s", local_error->message);
g_error_free (local_error);
@@ -738,35 +756,32 @@ composer_save_to_drafts_cleanup (GObject *source_object,
EActivity *activity;
EAlertSink *alert_sink;
GCancellable *cancellable;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
AsyncContext *async_context;
GError *local_error = NULL;
async_context = (AsyncContext *) user_data;
+ editor = e_msg_composer_get_editor (async_context->composer);
+ view = e_html_editor_get_view (editor);
+
activity = async_context->activity;
alert_sink = e_activity_get_alert_sink (activity);
cancellable = e_activity_get_cancellable (activity);
- e_mail_folder_append_message_finish (
- CAMEL_FOLDER (source_object), result,
- &async_context->message_uid, &local_error);
-
if (e_activity_handle_cancellation (activity, local_error)) {
- g_warn_if_fail (async_context->message_uid == NULL);
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
async_context_free (async_context);
g_error_free (local_error);
return;
} else if (local_error != NULL) {
- g_warn_if_fail (async_context->message_uid == NULL);
e_alert_submit (
alert_sink,
"mail-composer:save-to-drafts-error",
local_error->message, NULL);
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
async_context_free (async_context);
g_error_free (local_error);
return;
@@ -830,6 +845,8 @@ composer_save_to_drafts_got_folder (GObject *source_object,
{
EActivity *activity;
CamelFolder *drafts_folder;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
AsyncContext *async_context;
GError *local_error = NULL;
@@ -837,6 +854,9 @@ composer_save_to_drafts_got_folder (GObject *source_object,
activity = async_context->activity;
+ editor = e_msg_composer_get_editor (async_context->composer);
+ view = e_html_editor_get_view (editor);
+
drafts_folder = e_mail_session_uri_to_folder_finish (
E_MAIL_SESSION (source_object), result, &local_error);
@@ -846,8 +866,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
((drafts_folder == NULL) && (local_error != NULL)));
if (e_activity_handle_cancellation (activity, local_error)) {
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
async_context_free (async_context);
g_error_free (local_error);
return;
@@ -865,8 +884,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
GTK_WINDOW (async_context->composer),
"mail:ask-default-drafts", NULL);
if (response != GTK_RESPONSE_YES) {
- gtkhtml_editor_set_changed (
- GTKHTML_EDITOR (async_context->composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
async_context_free (async_context);
return;
}
@@ -1155,6 +1173,7 @@ em_utils_compose_new_message (EShell *shell,
composer = create_new_composer (shell, "", folder);
composer_set_no_change (composer);
+ e_msg_composer_is_from_new_message (composer, TRUE);
gtk_widget_show (GTK_WIDGET (composer));
@@ -1770,7 +1789,7 @@ forward_non_attached (EMailBackend *backend,
forward = quoting_text (QUOTING_FORWARD);
text = em_utils_message_to_html (
CAMEL_SESSION (session), message,
- forward, flags, NULL, NULL, &validity_found);
+ forward, flags, NULL, NULL, NULL, &validity_found);
if (text != NULL) {
CamelDataWrapper *content;
@@ -2818,16 +2837,10 @@ composer_set_body (EMsgComposer *composer,
gchar *text, *credits, *original;
CamelMimePart *part;
CamelSession *session;
- GSettings *settings;
- gboolean start_bottom, has_body_text = FALSE;
guint32 validity_found = 0;
session = e_msg_composer_ref_session (composer);
- settings = g_settings_new ("org.gnome.evolution.mail");
-
- start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
-
switch (style) {
case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
/* do nothing */
@@ -2842,9 +2855,9 @@ composer_set_body (EMsgComposer *composer,
original = quoting_text (QUOTING_ORIGINAL);
text = em_utils_message_to_html (
session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS,
- parts_list, start_bottom ? "<BR>" : NULL, &validity_found);
+ parts_list, "<span id=\"-x-evolution-reply-citation\">",
+ "</span>", &validity_found);
e_msg_composer_set_body_text (composer, text, TRUE);
- has_body_text = text && *text;
g_free (text);
g_free (original);
emu_update_composers_security (composer, validity_found);
@@ -2856,41 +2869,15 @@ composer_set_body (EMsgComposer *composer,
credits = attribution_format (message);
text = em_utils_message_to_html (
session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE,
- parts_list, start_bottom ? "<BR>" : NULL, &validity_found);
+ parts_list, "<span id=\"-x-evolution-reply-citation\">",
+ "</span>", &validity_found);
g_free (credits);
e_msg_composer_set_body_text (composer, text, TRUE);
- has_body_text = text && *text;
g_free (text);
emu_update_composers_security (composer, validity_found);
break;
}
- if (has_body_text && start_bottom) {
- GtkhtmlEditor *editor = GTKHTML_EDITOR (composer);
- gboolean move_cursor_to_end;
- gboolean top_signature;
-
- /* If we are placing signature on top, then move cursor to the end,
- * otherwise try to find the signature place and place cursor just
- * before the signature. We added there an empty line already. */
- gtkhtml_editor_run_command (editor, "block-selection");
- gtkhtml_editor_run_command (editor, "cursor-bod");
-
- top_signature = g_settings_get_boolean (settings, "composer-top-signature");
-
- move_cursor_to_end = top_signature ||
- !gtkhtml_editor_search_by_data (
- editor, 1, "ClueFlow", "signature", "1");
-
- if (move_cursor_to_end)
- gtkhtml_editor_run_command (editor, "cursor-eod");
- else
- gtkhtml_editor_run_command (editor, "selection-move-left");
- gtkhtml_editor_run_command (editor, "unblock-selection");
- }
-
- g_object_unref (settings);
-
g_object_unref (session);
}
@@ -2900,13 +2887,14 @@ em_utils_construct_composer_text (CamelSession *session,
EMailPartList *parts_list)
{
gchar *text, *credits;
+ gboolean start_bottom = FALSE;
g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
credits = attribution_format (message);
text = em_utils_message_to_html (
session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE,
- parts_list, NULL, NULL);
+ parts_list, NULL, start_bottom ? "<BR>" : NULL, NULL);
g_free (credits);
return text;
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 2291e0869b..f1f23d364d 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -1191,6 +1191,7 @@ em_utils_message_to_html (CamelSession *session,
const gchar *credits,
guint32 flags,
EMailPartList *parts_list,
+ const gchar *prepend,
const gchar *append,
EMailPartValidityFlags *validity_found)
{
@@ -1265,6 +1266,10 @@ em_utils_message_to_html (CamelSession *session,
if (validity_found != NULL)
*validity_found = is_validity_found;
+ if (prepend != NULL && *prepend != '\0')
+ g_output_stream_write_all (
+ stream, prepend, strlen (prepend), NULL, NULL, NULL);
+
e_mail_formatter_format_sync (
formatter, parts_list, stream, 0,
E_MAIL_FORMATTER_MODE_PRINTING, NULL);
diff --git a/mail/em-utils.h b/mail/em-utils.h
index 6725a0692e..907a91b7d7 100644
--- a/mail/em-utils.h
+++ b/mail/em-utils.h
@@ -64,6 +64,7 @@ gchar * em_utils_message_to_html (CamelSession *session,
const gchar *credits,
guint32 flags,
struct _EMailPartList *parts_list,
+ const gchar *prepend,
const gchar *append,
EMailPartValidityFlags *validity_found);
diff --git a/mail/importers/Makefile.am b/mail/importers/Makefile.am
index a1843c5098..d9beee985f 100644
--- a/mail/importers/Makefile.am
+++ b/mail/importers/Makefile.am
@@ -9,8 +9,8 @@ libevolution_mail_importers_la_CPPFLAGS = \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_mail_importers_la_SOURCES = \
mail-importer.c \
@@ -28,6 +28,6 @@ libevolution_mail_importers_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
-include $(top_srcdir)/git.mk
diff --git a/mail/mail-config.ui b/mail/mail-config.ui
index ddc5b89fa1..7b7e7c1ce1 100644
--- a/mail/mail-config.ui
+++ b/mail/mail-config.ui
@@ -173,6 +173,13 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
+ <object class="GtkAdjustment" id="adjustment7">
+ <property name="upper">200</property>
+ <property name="lower">31</property>
+ <property name="value">71</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">1</property>
+ </object>
<object class="GtkNotebook" id="composer_toplevel">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -294,6 +301,49 @@
</packing>
</child>
<child>
+ <object class="GtkHBox" id="hboxWrapCharactersCount">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lblWordWrapLength">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Number of characters for word w_rapping:</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinWordWrapLength">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">3</property>
+ <property name="adjustment">adjustment7</property>
+ <property name="caps_lock_warning">False</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkHBox" id="hboxComposerCharset">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -320,7 +370,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">5</property>
+ <property name="position">6</property>
</packing>
</child>
</object>
@@ -797,47 +847,6 @@
<property name="position">0</property>
</packing>
</child>
- <child>
- <object class="GtkHBox" id="hboxSpellCheckColor">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="lblSpellCheckColor">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Color for _misspelled words:</property>
- <property name="use_underline">True</property>
- <property name="justify">center</property>
- <property name="mnemonic_widget">colorButtonSpellCheckColor</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkColorButton" id="colorButtonSpellCheckColor">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="title" translatable="yes">Pick a color</property>
- <property name="color">#000000000000</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
</object>
</child>
</object>
diff --git a/modules/addressbook/Makefile.am b/modules/addressbook/Makefile.am
index fc2a562197..d84247a573 100644
--- a/modules/addressbook/Makefile.am
+++ b/modules/addressbook/Makefile.am
@@ -19,9 +19,9 @@ module_addressbook_la_CPPFLAGS = \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
$(CHAMPLAIN_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LDAP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_addressbook_la_SOURCES = \
evolution-module-addressbook.c \
@@ -64,8 +64,8 @@ module_addressbook_la_LIBADD = \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
$(CHAMPLAIN_LIBS) \
- $(GTKHTML_LIBS) \
- $(LDAP_LIBS)
+ $(LDAP_LIBS) \
+ $(NULL)
module_addressbook_la_LDFLAGS = \
diff --git a/modules/backup-restore/Makefile.am b/modules/backup-restore/Makefile.am
index 98d130197f..9dffc4a3df 100644
--- a/modules/backup-restore/Makefile.am
+++ b/modules/backup-restore/Makefile.am
@@ -14,7 +14,6 @@ module_backup_restore_la_CPPFLAGS = \
-DLIBDIR=\""$(libdir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -33,7 +32,6 @@ module_backup_restore_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_backup_restore_la_LDFLAGS = \
@@ -53,7 +51,6 @@ evolution_backup_CPPFLAGS = \
-DDBUS_SERVICES_DIR=\"'${datadir}'/dbus-1/services\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(NULL)
evolution_backup_SOURCES = \
@@ -64,7 +61,6 @@ evolution_backup_LDADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
if OS_WIN32
diff --git a/modules/backup-restore/evolution-backup-tool.c b/modules/backup-restore/evolution-backup-tool.c
index 4b75f8a790..728b8f96f4 100644
--- a/modules/backup-restore/evolution-backup-tool.c
+++ b/modules/backup-restore/evolution-backup-tool.c
@@ -101,38 +101,6 @@ static GOptionEntry options[] = {
static gboolean check (const gchar *filename, gboolean *is_new_format);
-static GString *
-replace_string (const gchar *text,
- const gchar *find,
- const gchar *replace)
-{
- const gchar *p, *next;
- GString *str;
- gint find_len;
-
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (find != NULL, NULL);
- g_return_val_if_fail (*find, NULL);
-
- find_len = strlen (find);
- str = g_string_new ("");
-
- p = text;
- while (next = strstr (p, find), next) {
- if (p < next)
- g_string_append_len (str, p, next - p);
-
- if (replace && *replace)
- g_string_append (str, replace);
-
- p = next + find_len;
- }
-
- g_string_append (str, p);
-
- return str;
-}
-
static const gchar *
strip_home_dir (const gchar *dir)
{
@@ -166,11 +134,11 @@ replace_variables (const gchar *str,
strip_datadir = strip_home_dir (e_get_user_data_dir ());
strip_configdir = strip_home_dir (e_get_user_config_dir ());
- #define repl(_find, _replace) \
- use = replace_string (res ? res->str : str, _find, _replace); \
- g_return_val_if_fail (use != NULL, NULL); \
- if (res) \
- g_string_free (res, TRUE); \
+ #define repl(_find, _replace) \
+ use = e_str_replace_string (res ? res->str : str, _find, _replace); \
+ g_return_val_if_fail (use != NULL, NULL); \
+ if (res) \
+ g_string_free (res, TRUE); \
res = use;
repl ("$HOME", g_get_home_dir ());
@@ -223,7 +191,7 @@ replace_in_file (const gchar *filename,
}
if (g_file_get_contents (filename, &content, NULL, &error)) {
- GString *str = replace_string (content, find, replace);
+ GString *str = e_str_replace_string (content, find, replace);
if (str) {
if (!g_file_set_contents (filename, str->str, -1, &error) && error) {
diff --git a/modules/book-config-google/Makefile.am b/modules/book-config-google/Makefile.am
index 0de66dc9b5..cdd75f74bb 100644
--- a/modules/book-config-google/Makefile.am
+++ b/modules/book-config-google/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_google_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-book-config-google\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_book_config_google_la_SOURCES = \
evolution-book-config-google.c
@@ -16,7 +16,7 @@ module_book_config_google_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_book_config_google_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-ldap/Makefile.am b/modules/book-config-ldap/Makefile.am
index aa2bdb8996..7f73e4ca16 100644
--- a/modules/book-config-ldap/Makefile.am
+++ b/modules/book-config-ldap/Makefile.am
@@ -6,9 +6,9 @@ module_book_config_ldap_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-book-config-ldap\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LDAP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_book_config_ldap_la_SOURCES = \
evolution-book-config-ldap.c \
@@ -19,8 +19,8 @@ module_book_config_ldap_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(LDAP_LIBS)
+ $(LDAP_LIBS) \
+ $(NULL)
module_book_config_ldap_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-local/Makefile.am b/modules/book-config-local/Makefile.am
index 8c1d26b9ea..f7442bef80 100644
--- a/modules/book-config-local/Makefile.am
+++ b/modules/book-config-local/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_local_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-book-config-local\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_book_config_local_la_SOURCES = \
evolution-book-config-local.c
@@ -16,7 +16,7 @@ module_book_config_local_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_book_config_local_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-webdav/Makefile.am b/modules/book-config-webdav/Makefile.am
index 25a8f00624..7e95f6ffe1 100644
--- a/modules/book-config-webdav/Makefile.am
+++ b/modules/book-config-webdav/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_webdav_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-book-config-webdav\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_book_config_webdav_la_SOURCES = \
evolution-book-config-webdav.c
@@ -16,7 +16,7 @@ module_book_config_webdav_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_book_config_webdav_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-caldav/Makefile.am b/modules/cal-config-caldav/Makefile.am
index 03126c434e..95ffc2a1b9 100644
--- a/modules/cal-config-caldav/Makefile.am
+++ b/modules/cal-config-caldav/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_caldav_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-caldav\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBSOUP_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_caldav_la_SOURCES = \
evolution-cal-config-caldav.c \
@@ -21,8 +21,8 @@ module_cal_config_caldav_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(LIBSOUP_LIBS)
+ $(LIBSOUP_LIBS) \
+ $(NULL)
module_cal_config_caldav_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-contacts/Makefile.am b/modules/cal-config-contacts/Makefile.am
index 4a1e391f88..2c3cc4f908 100644
--- a/modules/cal-config-contacts/Makefile.am
+++ b/modules/cal-config-contacts/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_contacts_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-contacts\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_contacts_la_SOURCES = \
evolution-cal-config-contacts.c \
@@ -20,7 +20,7 @@ module_cal_config_contacts_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_cal_config_contacts_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-google/Makefile.am b/modules/cal-config-google/Makefile.am
index 5ebdf56ff0..2f1a3defd6 100644
--- a/modules/cal-config-google/Makefile.am
+++ b/modules/cal-config-google/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_google_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-google\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(GDATA_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_google_la_SOURCES = \
evolution-cal-config-google.c \
@@ -23,8 +23,8 @@ module_cal_config_google_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(GDATA_LIBS)
+ $(GDATA_LIBS) \
+ $(NULL)
module_cal_config_google_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-local/Makefile.am b/modules/cal-config-local/Makefile.am
index 2d4d1a0f98..c503a02734 100644
--- a/modules/cal-config-local/Makefile.am
+++ b/modules/cal-config-local/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_local_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-local\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_local_la_SOURCES = \
evolution-cal-config-local.c \
@@ -18,7 +18,7 @@ module_cal_config_local_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_cal_config_local_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-weather/Makefile.am b/modules/cal-config-weather/Makefile.am
index f1a22f0cd9..54c4850393 100644
--- a/modules/cal-config-weather/Makefile.am
+++ b/modules/cal-config-weather/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_weather_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-weather\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(GWEATHER_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_weather_la_SOURCES = \
evolution-cal-config-weather.c \
@@ -19,8 +19,8 @@ module_cal_config_weather_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(GWEATHER_LIBS)
+ $(GWEATHER_LIBS) \
+ $(NULL)
module_cal_config_weather_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-webcal/Makefile.am b/modules/cal-config-webcal/Makefile.am
index 43c0d25402..36cacf6c0e 100644
--- a/modules/cal-config-webcal/Makefile.am
+++ b/modules/cal-config-webcal/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_webcal_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-cal-config-webcal\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_cal_config_webcal_la_SOURCES = \
evolution-cal-config-webcal.c
@@ -16,7 +16,7 @@ module_cal_config_webcal_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_cal_config_webcal_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/calendar/Makefile.am b/modules/calendar/Makefile.am
index 348da1beb7..ce02506534 100644
--- a/modules/calendar/Makefile.am
+++ b/modules/calendar/Makefile.am
@@ -9,8 +9,8 @@ module_calendar_la_CPPFLAGS = \
-DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_calendar_la_SOURCES = \
evolution-module-calendar.c \
@@ -77,7 +77,7 @@ module_calendar_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_calendar_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/composer-autosave/Makefile.am b/modules/composer-autosave/Makefile.am
index a3c16be5eb..10cae2de7c 100644
--- a/modules/composer-autosave/Makefile.am
+++ b/modules/composer-autosave/Makefile.am
@@ -1,34 +1,29 @@
-NULL =
-
module_LTLIBRARIES = module-composer-autosave.la
module_composer_autosave_la_CPPFLAGS = \
- $(AM_CPPFLAGS) \
- -I$(top_srcdir) \
- -DG_LOG_DOMAIN=\"evolution-composer-autosave\" \
- $(EVOLUTION_DATA_SERVER_CFLAGS) \
- $(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS) \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"evolution-composer-autosave\" \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
$(NULL)
module_composer_autosave_la_SOURCES = \
- evolution-composer-autosave.c \
- e-autosave-utils.c \
- e-autosave-utils.h \
- e-composer-autosave.c \
- e-composer-autosave.h \
- e-composer-registry.c \
- e-composer-registry.h \
- $(NULL)
+ evolution-composer-autosave.c \
+ e-autosave-utils.c \
+ e-autosave-utils.h \
+ e-composer-autosave.c \
+ e-composer-autosave.h \
+ e-composer-registry.c \
+ e-composer-registry.h
module_composer_autosave_la_LIBADD = \
- $(top_builddir)/shell/libevolution-shell.la \
- $(top_builddir)/composer/libevolution-mail-composer.la \
- $(top_builddir)/e-util/libevolution-util.la \
- $(EVOLUTION_DATA_SERVER_LIBS) \
- $(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
+ $(top_builddir)/shell/libevolution-shell.la \
+ $(top_builddir)/composer/libevolution-mail-composer.la \
+ $(top_builddir)/e-util/libevolution-util.la \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
$(NULL)
module_composer_autosave_la_LDFLAGS = \
diff --git a/modules/composer-autosave/e-composer-autosave.c b/modules/composer-autosave/e-composer-autosave.c
index 33d6c0203e..d75532c675 100644
--- a/modules/composer-autosave/e-composer-autosave.c
+++ b/modules/composer-autosave/e-composer-autosave.c
@@ -125,13 +125,15 @@ composer_autosave_timeout_cb (gpointer user_data)
static void
composer_autosave_changed_cb (EComposerAutosave *autosave)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
EExtensible *extensible;
extensible = e_extension_get_extensible (E_EXTENSION (autosave));
- editor = GTKHTML_EDITOR (extensible);
- autosave->priv->changed = gtkhtml_editor_get_changed (editor);
+ editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
+ view = e_html_editor_get_view (editor);
+ autosave->priv->changed = e_html_editor_view_get_changed (view);
if (autosave->priv->changed && autosave->priv->timeout_id == 0) {
autosave->priv->timeout_id = e_named_timeout_add_seconds (
@@ -164,6 +166,8 @@ composer_autosave_dispose (GObject *object)
static void
composer_autosave_constructed (GObject *object)
{
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
EExtensible *extensible;
/* Chain up to parent's constructed() method. */
@@ -171,9 +175,11 @@ composer_autosave_constructed (GObject *object)
constructed (object);
extensible = e_extension_get_extensible (E_EXTENSION (object));
+ editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
+ view = e_html_editor_get_view (editor);
e_signal_connect_notify_swapped (
- extensible, "notify::changed",
+ view, "notify::changed",
G_CALLBACK (composer_autosave_changed_cb), object);
}
diff --git a/modules/contact-photos/Makefile.am b/modules/contact-photos/Makefile.am
index 100ed9a359..5f2b19dc92 100644
--- a/modules/contact-photos/Makefile.am
+++ b/modules/contact-photos/Makefile.am
@@ -6,7 +6,6 @@ module_contact_photos_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-contact-photos\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -22,7 +21,6 @@ module_contact_photos_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_contact_photos_la_LDFLAGS = \
diff --git a/modules/gravatar/Makefile.am b/modules/gravatar/Makefile.am
index 874a403d29..4613ea995c 100644
--- a/modules/gravatar/Makefile.am
+++ b/modules/gravatar/Makefile.am
@@ -6,7 +6,6 @@ module_gravatar_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-gravatar\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -22,7 +21,6 @@ module_gravatar_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_gravatar_la_LDFLAGS = \
diff --git a/modules/itip-formatter/Makefile.am b/modules/itip-formatter/Makefile.am
index 6d7e62ff39..2e1168bdd1 100644
--- a/modules/itip-formatter/Makefile.am
+++ b/modules/itip-formatter/Makefile.am
@@ -11,8 +11,8 @@ module_itip_formatter_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-itip-formatter\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_itip_formatter_la_SOURCES = \
e-conflict-search-selector.c \
@@ -38,7 +38,7 @@ module_itip_formatter_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_itip_formatter_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/itip-formatter/itip-view.c b/modules/itip-formatter/itip-view.c
index f19c418923..64d075e4fa 100644
--- a/modules/itip-formatter/itip-view.c
+++ b/modules/itip-formatter/itip-view.c
@@ -791,13 +791,7 @@ alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
WebKitDOMElement *check2;
gchar *id;
-#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */
- id = webkit_dom_element_get_id (
- WEBKIT_DOM_ELEMENT (check1));
-#else
- id = webkit_dom_html_element_get_id (
- WEBKIT_DOM_HTML_ELEMENT (check1));
-#endif
+ id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1));
if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
check2 = webkit_dom_document_get_element_by_id (
@@ -906,11 +900,7 @@ append_info_item_row (ItipView *view,
WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
id = g_strdup_printf ("%s_row_%d", table_id, item->id);
-#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */
webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), id);
-#else
- webkit_dom_html_element_set_id (row, id);
-#endif
g_free (id);
switch (item->type) {
@@ -932,7 +922,7 @@ append_info_item_row (ItipView *view,
}
cell = webkit_dom_html_table_row_element_insert_cell (
- (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
if (icon_name) {
WebKitDOMElement *image;
@@ -953,7 +943,7 @@ append_info_item_row (ItipView *view,
}
cell = webkit_dom_html_table_row_element_insert_cell (
- (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+ WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
diff --git a/modules/itip-formatter/plugin/Makefile.am b/modules/itip-formatter/plugin/Makefile.am
index ac23084824..30c75b83e3 100644
--- a/modules/itip-formatter/plugin/Makefile.am
+++ b/modules/itip-formatter/plugin/Makefile.am
@@ -10,8 +10,8 @@ liborg_gnome_itip_formatter_la_CPPFLAGS = \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_itip_formatter_la_SOURCES = \
config-ui.c \
@@ -30,7 +30,7 @@ liborg_gnome_itip_formatter_la_LIBADD = \
$(top_builddir)/em-format/libevolution-mail-formatter.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
BUILT_SOURCES = $(plugin_DATA)
diff --git a/modules/mail-config/Makefile.am b/modules/mail-config/Makefile.am
index 1a84b9e16e..c1d9eae2c0 100644
--- a/modules/mail-config/Makefile.am
+++ b/modules/mail-config/Makefile.am
@@ -8,7 +8,6 @@ module_mail_config_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-mail-config\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -32,7 +31,6 @@ module_mail_config_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_mail_config_la_LDFLAGS = \
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
index 595c35743a..770975efd7 100644
--- a/modules/mail/Makefile.am
+++ b/modules/mail/Makefile.am
@@ -9,8 +9,8 @@ module_mail_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-mail\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_mail_la_SOURCES = \
evolution-module-mail.c \
@@ -51,7 +51,7 @@ module_mail_la_LIBADD = \
$(libevolution_mail_settings_la) \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_mail_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index e74086f204..321e1032e3 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -475,21 +475,26 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
EShell *shell = E_SHELL (application);
EMailBackend *backend;
EMailSession *session;
+ EHTMLEditor *editor = NULL;
const gchar *backend_name;
backend = E_MAIL_BACKEND (shell_backend);
session = e_mail_backend_get_session (backend);
+ if (E_IS_MSG_COMPOSER (window))
+ editor = e_msg_composer_get_editor (E_MSG_COMPOSER (window));
+
+ if (E_IS_MAIL_SIGNATURE_EDITOR (window))
+ editor = e_mail_signature_editor_get_editor (
+ E_MAIL_SIGNATURE_EDITOR (window));
+
/* This applies to both the composer and signature editor. */
- if (GTKHTML_IS_EDITOR (window)) {
+ if (editor != NULL) {
+ EHTMLEditorView *view;
GSettings *settings;
- GList *spell_languages;
gboolean active = TRUE;
- spell_languages = e_load_spell_languages ();
- gtkhtml_editor_set_spell_languages (
- GTKHTML_EDITOR (window), spell_languages);
- g_list_free (spell_languages);
+ view = e_html_editor_get_view (editor);
settings = g_settings_new ("org.gnome.evolution.mail");
@@ -498,7 +503,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
g_object_unref (settings);
- gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (window), active);
+ e_html_editor_view_set_html_mode (view, active);
}
if (E_IS_MSG_COMPOSER (window)) {
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index aff084d522..14b08300b5 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -265,8 +265,7 @@ mail_shell_view_mail_display_needs_key (EMailDisplay *mail_display,
if (!frame)
return FALSE;
dom = webkit_web_frame_get_dom_document (frame);
- /* intentionally used "static_cast" */
- element = webkit_dom_html_document_get_active_element ((WebKitDOMHTMLDocument *) dom);
+ element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom));
if (element)
name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index 2925f4e63c..e42222d8b0 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -24,7 +24,6 @@
#include "e-mail-shell-view.h"
#include <glib/gi18n.h>
-#include <gtkhtml/gtkhtml.h>
#include <camel/camel-search-private.h> /* for camel_search_word */
#include <mail/e-mail-folder-create-dialog.h>
diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c
index 3861519509..8ec3f6faf3 100644
--- a/modules/mail/em-composer-prefs.c
+++ b/modules/mail/em-composer-prefs.c
@@ -34,10 +34,6 @@
#include <glib/gi18n.h>
#include <glib/gstdio.h>
-#include <gtkhtml/gtkhtml.h>
-#include <editor/gtkhtml-spell-language.h>
-#include <libedataserver/libedataserver.h>
-
#include <composer/e-msg-composer.h>
#include <shell/e-shell-utils.h>
@@ -56,53 +52,6 @@ G_DEFINE_TYPE (
em_composer_prefs,
GTK_TYPE_VBOX)
-static gboolean
-composer_prefs_map_string_to_color (GValue *value,
- GVariant *variant,
- gpointer user_data)
-{
- GdkColor color;
- const gchar *string;
- gboolean success = FALSE;
-
- string = g_variant_get_string (variant, NULL);
- if (gdk_color_parse (string, &color)) {
- g_value_set_boxed (value, &color);
- success = TRUE;
- }
-
- return success;
-}
-
-static GVariant *
-composer_prefs_map_color_to_string (const GValue *value,
- const GVariantType *expected_type,
- gpointer user_data)
-{
- GVariant *variant;
- const GdkColor *color;
-
- color = g_value_get_boxed (value);
- if (color == NULL) {
- variant = g_variant_new_string ("");
- } else {
- gchar *string;
-
- /* Encode the color manually because CSS styles expect
- * color codes as #rrggbb, whereas gdk_color_to_string()
- * returns color codes as #rrrrggggbbbb. */
- string = g_strdup_printf (
- "#%02x%02x%02x",
- (gint) color->red * 256 / 65536,
- (gint) color->green * 256 / 65536,
- (gint) color->blue * 256 / 65536);
- variant = g_variant_new_string (string);
- g_free (string);
- }
-
- return variant;
-}
-
static void
composer_prefs_dispose (GObject *object)
{
@@ -168,7 +117,7 @@ spell_language_save (EMComposerPrefs *prefs)
/* Build a list of active spell languages. */
valid = gtk_tree_model_get_iter_first (model, &iter);
while (valid) {
- const GtkhtmlSpellLanguage *language;
+ ESpellDictionary *language;
gboolean active;
gtk_tree_model_get (
@@ -191,36 +140,36 @@ spell_language_save (EMComposerPrefs *prefs)
static void
spell_setup (EMComposerPrefs *prefs)
{
- const GList *available_languages;
- GList *active_languages;
+ GList *list, *link;
GtkListStore *store;
store = GTK_LIST_STORE (prefs->language_model);
- available_languages = gtkhtml_spell_language_get_available ();
- active_languages = e_load_spell_languages ();
+ list = e_spell_checker_list_available_dicts (prefs->spell_checker);
/* Populate the GtkListStore. */
- while (available_languages != NULL) {
- const GtkhtmlSpellLanguage *language;
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESpellDictionary *dictionary;
GtkTreeIter tree_iter;
const gchar *name;
+ const gchar *code;
gboolean active;
- language = available_languages->data;
- name = gtkhtml_spell_language_get_name (language);
- active = (g_list_find (active_languages, language) != NULL);
+ dictionary = E_SPELL_DICTIONARY (link->data);
+ name = e_spell_dictionary_get_name (dictionary);
+ code = e_spell_dictionary_get_code (dictionary);
+
+ active = e_spell_checker_get_language_active (
+ prefs->spell_checker, code);
gtk_list_store_append (store, &tree_iter);
gtk_list_store_set (
store, &tree_iter,
- 0, active, 1, name, 2, language, -1);
-
- available_languages = available_languages->next;
+ 0, active, 1, name, 2, dictionary, -1);
}
- g_list_free (active_languages);
+ g_list_free (list);
}
#define MAIL_SEND_ACCOUNT_OVERRIDE_KEY "sao-mail-send-account-override"
@@ -1006,10 +955,15 @@ static EMConfigItem emcp_items[] = {
(gchar *) "vboxSpellChecking",
emcp_widget_glade },
- { E_CONFIG_PAGE,
- (gchar *) "90.accountoverride",
- (gchar *) "send-account-override-grid",
- emcp_widget_glade }
+ { E_CONFIG_SECTION_TABLE,
+ (gchar *) "20.spellcheck/00.languages",
+ (gchar *) "languages-table",
+ emcp_widget_glade },
+
+ { E_CONFIG_SECTION,
+ (gchar *) "20.spellcheck/00.options",
+ (gchar *) "spell-options-vbox",
+ emcp_widget_glade },
};
static void
@@ -1051,6 +1005,8 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
prefs->builder = gtk_builder_new ();
e_load_ui_builder_definition (prefs->builder, "mail-config.ui");
+ prefs->spell_checker = e_spell_checker_new ();
+
/** @HookPoint-EMConfig: Mail Composer Preferences
* @Id: org.gnome.evolution.mail.composerPrefs
* @Class: org.gnome.evolution.mail.config:1.0
@@ -1134,6 +1090,12 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
widget, "active",
G_SETTINGS_BIND_DEFAULT);
+ widget = e_builder_get_widget (prefs->builder, "spinWordWrapLength");
+ g_settings_bind (
+ settings, "composer-word-wrap-length",
+ widget, "value",
+ G_SETTINGS_BIND_DEFAULT);
+
widget = e_builder_get_widget (prefs->builder, "chkOutlookFilenames");
g_settings_bind (
settings, "composer-outlook-filenames",
@@ -1193,9 +1155,6 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
view = GTK_TREE_VIEW (widget);
store = gtk_list_store_new (
3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
- g_signal_connect_swapped (
- store, "row-changed",
- G_CALLBACK (spell_language_save), prefs);
prefs->language_model = GTK_TREE_MODEL (store);
gtk_tree_view_set_model (view, prefs->language_model);
renderer = gtk_cell_renderer_toggle_new ();
@@ -1215,19 +1174,14 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
info_pixmap = e_builder_get_widget (prefs->builder, "pixmapSpellInfo");
gtk_image_set_from_icon_name (
GTK_IMAGE (info_pixmap),
- "dialog-information", GTK_ICON_SIZE_BUTTON);
-
- widget = e_builder_get_widget (prefs->builder, "colorButtonSpellCheckColor");
- g_settings_bind_with_mapping (
- settings, "composer-spell-color",
- widget, "color",
- G_SETTINGS_BIND_DEFAULT,
- composer_prefs_map_string_to_color,
- composer_prefs_map_color_to_string,
- NULL, (GDestroyNotify) NULL);
+ GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
spell_setup (prefs);
+ g_signal_connect_swapped (
+ store, "row-changed",
+ G_CALLBACK (spell_language_save), prefs);
+
/* Forwards and Replies */
widget = e_builder_get_widget (prefs->builder, "comboboxForwardStyle");
g_settings_bind (
diff --git a/modules/mail/em-composer-prefs.h b/modules/mail/em-composer-prefs.h
index cb986f597d..5b72293c9d 100644
--- a/modules/mail/em-composer-prefs.h
+++ b/modules/mail/em-composer-prefs.h
@@ -23,7 +23,6 @@
#define EM_COMPOSER_PREFS_H
#include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
#include <shell/e-shell.h>
@@ -67,7 +66,9 @@ struct _EMComposerPrefs {
GtkComboBox *reply_style;
/* Signatures */
- GtkHTML *sig_preview;
+ EWebViewPreview *sig_preview;
+
+ ESpellChecker *spell_checker;
};
struct _EMComposerPrefsClass {
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
index 3e3aa3909c..0e2b11beb5 100644
--- a/modules/mail/em-mailer-prefs.c
+++ b/modules/mail/em-mailer-prefs.c
@@ -28,7 +28,6 @@
#include "em-mailer-prefs.h"
-#include <gtkhtml/gtkhtml-properties.h>
#include <libxml/tree.h>
#include <shell/e-shell-utils.h>
@@ -738,7 +737,7 @@ image_loading_policy_always_cb (GtkToggleButton *toggle_button)
g_settings_set_enum (
settings, "image-loading-policy",
- E_MAIL_IMAGE_LOADING_POLICY_ALWAYS);
+ E_IMAGE_LOADING_POLICY_ALWAYS);
g_object_unref (settings);
}
@@ -754,7 +753,7 @@ image_loading_policy_sometimes_cb (GtkToggleButton *toggle_button)
g_settings_set_enum (
settings, "image-loading-policy",
- E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES);
+ E_IMAGE_LOADING_POLICY_SOMETIMES);
g_object_unref (settings);
}
@@ -770,7 +769,7 @@ image_loading_policy_never_cb (GtkToggleButton *toggle_button)
g_settings_set_enum (
settings, "image-loading-policy",
- E_MAIL_IMAGE_LOADING_POLICY_NEVER);
+ E_IMAGE_LOADING_POLICY_NEVER);
g_object_unref (settings);
}
@@ -1006,7 +1005,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
prefs->builder, "radImagesNever");
gtk_toggle_button_set_active (
GTK_TOGGLE_BUTTON (widget),
- val == E_MAIL_IMAGE_LOADING_POLICY_NEVER);
+ val == E_IMAGE_LOADING_POLICY_NEVER);
gtk_widget_set_sensitive (widget, writable);
g_signal_connect (
@@ -1017,7 +1016,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
prefs->builder, "radImagesSometimes");
gtk_toggle_button_set_active (
GTK_TOGGLE_BUTTON (widget),
- val == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES);
+ val == E_IMAGE_LOADING_POLICY_SOMETIMES);
gtk_widget_set_sensitive (widget, writable);
g_signal_connect (
@@ -1028,7 +1027,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
prefs->builder, "radImagesAlways");
gtk_toggle_button_set_active (
GTK_TOGGLE_BUTTON (widget),
- val == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS);
+ val == E_IMAGE_LOADING_POLICY_ALWAYS);
gtk_widget_set_sensitive (widget, writable);
g_signal_connect (
diff --git a/modules/mailto-handler/Makefile.am b/modules/mailto-handler/Makefile.am
index a4d90d34e8..624ca374a2 100644
--- a/modules/mailto-handler/Makefile.am
+++ b/modules/mailto-handler/Makefile.am
@@ -6,8 +6,8 @@ module_mailto_handler_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-mailto-handler\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_mailto_handler_la_SOURCES = \
evolution-mailto-handler.c
@@ -17,7 +17,7 @@ module_mailto_handler_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_mailto_handler_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/mdn/Makefile.am b/modules/mdn/Makefile.am
index 550aa08d83..ba43b997f5 100644
--- a/modules/mdn/Makefile.am
+++ b/modules/mdn/Makefile.am
@@ -6,8 +6,8 @@ module_mdn_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-mdn\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_mdn_la_SOURCES = \
evolution-mdn.c
@@ -19,7 +19,7 @@ module_mdn_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_mdn_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/offline-alert/Makefile.am b/modules/offline-alert/Makefile.am
index 68ef0d2620..8a2080315d 100644
--- a/modules/offline-alert/Makefile.am
+++ b/modules/offline-alert/Makefile.am
@@ -6,8 +6,8 @@ module_offline_alert_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-offline-alert\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_offline_alert_la_SOURCES = \
evolution-offline-alert.c
@@ -17,7 +17,7 @@ module_offline_alert_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_offline_alert_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/plugin-lib/Makefile.am b/modules/plugin-lib/Makefile.am
index 48506c047d..d94320a7e6 100644
--- a/modules/plugin-lib/Makefile.am
+++ b/modules/plugin-lib/Makefile.am
@@ -7,8 +7,8 @@ module_plugin_lib_la_CPPFLAGS = \
-DEVOLUTION_PREFIX=\""$(prefix)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_plugin_lib_la_SOURCES = \
evolution-module-plugin-lib.c \
@@ -19,7 +19,7 @@ module_plugin_lib_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_plugin_lib_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/plugin-manager/Makefile.am b/modules/plugin-manager/Makefile.am
index eef64c23a7..c00ce41239 100644
--- a/modules/plugin-manager/Makefile.am
+++ b/modules/plugin-manager/Makefile.am
@@ -6,8 +6,8 @@ module_plugin_manager_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-plugin-manager\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_plugin_manager_la_SOURCES = \
evolution-plugin-manager.c
@@ -17,7 +17,7 @@ module_plugin_manager_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_plugin_manager_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/prefer-plain/Makefile.am b/modules/prefer-plain/Makefile.am
index e3b546bb68..9b6c8b352f 100644
--- a/modules/prefer-plain/Makefile.am
+++ b/modules/prefer-plain/Makefile.am
@@ -9,8 +9,8 @@ module_prefer_plain_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-prefer-plain\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_prefer_plain_la_SOURCES = \
e-mail-parser-prefer-plain.c \
@@ -26,7 +26,7 @@ module_prefer_plain_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_prefer_plain_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/prefer-plain/plugin/Makefile.am b/modules/prefer-plain/plugin/Makefile.am
index b3179af307..e1f73c6d66 100644
--- a/modules/prefer-plain/plugin/Makefile.am
+++ b/modules/prefer-plain/plugin/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_prefer_plain_la_CPPFLAGS = \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_prefer_plain_la_SOURCES = \
config-ui.c
@@ -20,7 +20,7 @@ liborg_gnome_prefer_plain_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(
liborg_gnome_prefer_plain_la_LIBADD = \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
BUILT_SOURCES = $(plugin_DATA)
diff --git a/modules/settings/Makefile.am b/modules/settings/Makefile.am
index 3c9bdb0327..c03253db9e 100644
--- a/modules/settings/Makefile.am
+++ b/modules/settings/Makefile.am
@@ -8,7 +8,6 @@ module_settings_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-settings\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -46,12 +45,12 @@ module_settings_la_SOURCES = \
e-settings-message-list.h \
e-settings-name-selector-entry.c \
e-settings-name-selector-entry.h \
+ e-settings-spell-checker.c \
+ e-settings-spell-checker.h \
e-settings-spell-entry.c \
e-settings-spell-entry.h \
e-settings-web-view.c \
e-settings-web-view.h \
- e-settings-web-view-gtkhtml.c \
- e-settings-web-view-gtkhtml.h \
e-settings-weekday-chooser.c \
e-settings-weekday-chooser.h \
$(NULL)
@@ -65,7 +64,6 @@ module_settings_la_LIBADD = \
$(top_builddir)/calendar/gui/libevolution-calendar.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_settings_la_LDFLAGS = \
diff --git a/modules/settings/e-settings-deprecated.c b/modules/settings/e-settings-deprecated.c
index b34e791b0d..d519a91e0e 100644
--- a/modules/settings/e-settings-deprecated.c
+++ b/modules/settings/e-settings-deprecated.c
@@ -332,7 +332,7 @@ static void
settings_deprecated_image_loading_policy_cb (GSettings *settings,
const gchar *key)
{
- EMailImageLoadingPolicy policy;
+ EImageLoadingPolicy policy;
policy = g_settings_get_enum (settings, "image-loading-policy");
e_settings_deprecated_set_int_with_change_test (settings, "load-http-images", policy);
diff --git a/modules/settings/e-settings-spell-checker.c b/modules/settings/e-settings-spell-checker.c
new file mode 100644
index 0000000000..6e5f0cc843
--- /dev/null
+++ b/modules/settings/e-settings-spell-checker.c
@@ -0,0 +1,117 @@
+/*
+ * e-settings-spell-checker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-settings-spell-checker.h"
+
+#include <e-util/e-util.h>
+
+#define E_SETTINGS_SPELL_CHECKER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerPrivate))
+
+struct _ESettingsSpellCheckerPrivate {
+ gint placeholder;
+};
+
+G_DEFINE_DYNAMIC_TYPE (
+ ESettingsSpellChecker,
+ e_settings_spell_checker,
+ E_TYPE_EXTENSION)
+
+static ESpellChecker *
+settings_spell_checker_get_extensible (ESettingsSpellChecker *extension)
+{
+ EExtensible *extensible;
+
+ extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+ return E_SPELL_CHECKER (extensible);
+}
+
+static void
+settings_spell_checker_constructed (GObject *object)
+{
+ ESpellChecker *spell_checker;
+ GSettings *settings;
+ gchar **strv;
+ guint ii;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_settings_spell_checker_parent_class)->
+ constructed (object);
+
+ /* This only initializes the active spell languages, it does not
+ * write changes back to GSettings. Only the ESpellChecker used
+ * in Composer Preferences should be doing that. */
+
+ spell_checker = settings_spell_checker_get_extensible (
+ E_SETTINGS_SPELL_CHECKER (object));
+
+ /* Make sure there are no active languages at this point. */
+ g_warn_if_fail (
+ e_spell_checker_count_active_languages (spell_checker) == 0);
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ strv = g_settings_get_strv (settings, "composer-spell-languages");
+ g_object_unref (settings);
+
+ g_return_if_fail (strv != NULL);
+
+ for (ii = 0; strv[ii] != NULL; ii++)
+ e_spell_checker_set_language_active (
+ spell_checker, strv[ii], TRUE);
+
+ g_strfreev (strv);
+}
+
+static void
+e_settings_spell_checker_class_init (ESettingsSpellCheckerClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ g_type_class_add_private (
+ class, sizeof (ESettingsSpellCheckerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = settings_spell_checker_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_SPELL_CHECKER;
+}
+
+static void
+e_settings_spell_checker_class_finalize (ESettingsSpellCheckerClass *class)
+{
+}
+
+static void
+e_settings_spell_checker_init (ESettingsSpellChecker *extension)
+{
+ extension->priv = E_SETTINGS_SPELL_CHECKER_GET_PRIVATE (extension);
+}
+
+void
+e_settings_spell_checker_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_settings_spell_checker_register_type (type_module);
+}
+
diff --git a/modules/settings/e-settings-spell-checker.h b/modules/settings/e-settings-spell-checker.h
new file mode 100644
index 0000000000..3e60cecf06
--- /dev/null
+++ b/modules/settings/e-settings-spell-checker.h
@@ -0,0 +1,65 @@
+/*
+ * e-settings-spell-checker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_SETTINGS_SPELL_CHECKER_H
+#define E_SETTINGS_SPELL_CHECKER_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SETTINGS_SPELL_CHECKER \
+ (e_settings_spell_checker_get_type ())
+#define E_SETTINGS_SPELL_CHECKER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellChecker))
+#define E_SETTINGS_SPELL_CHECKER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerClass))
+#define E_IS_SETTINGS_SPELL_CHECKER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SETTINGS_SPELL_CHECKER))
+#define E_IS_SETTINGS_SPELL_CHECKER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SETTINGS_SPELL_CHECKER))
+#define E_SETTINGS_SPELL_CHECKER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESettingsSpellChecker ESettingsSpellChecker;
+typedef struct _ESettingsSpellCheckerClass ESettingsSpellCheckerClass;
+typedef struct _ESettingsSpellCheckerPrivate ESettingsSpellCheckerPrivate;
+
+struct _ESettingsSpellChecker {
+ EExtension parent;
+ ESettingsSpellCheckerPrivate *priv;
+};
+
+struct _ESettingsSpellCheckerClass {
+ EExtensionClass parent_class;
+};
+
+GType e_settings_spell_checker_get_type
+ (void) G_GNUC_CONST;
+void e_settings_spell_checker_type_register
+ (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_SETTINGS_SPELL_CHECKER_H */
diff --git a/modules/settings/e-settings-web-view-gtkhtml.c b/modules/settings/e-settings-web-view-gtkhtml.c
deleted file mode 100644
index 7815eb6a17..0000000000
--- a/modules/settings/e-settings-web-view-gtkhtml.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * e-settings-web-view-gtkhtml.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-
-#include "e-settings-web-view-gtkhtml.h"
-
-#include <e-util/e-util.h>
-
-#define E_SETTINGS_WEB_VIEW_GTKHTML_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML, ESettingsWebViewGtkHTMLPrivate))
-
-struct _ESettingsWebViewGtkHTMLPrivate {
- GtkCssProvider *css_provider;
- GSettings *settings;
- GHashTable *old_values;
-};
-
-G_DEFINE_DYNAMIC_TYPE (
- ESettingsWebViewGtkHTML,
- e_settings_web_view_gtkhtml,
- E_TYPE_EXTENSION)
-
-/* replaces content of color string */
-static void
-settings_web_view_gtkhtml_fix_color_string (gchar *color_string)
-{
- GdkColor color;
-
- if (color_string == NULL)
- return;
-
- if (strlen (color_string) < 13)
- return;
-
- if (!gdk_color_parse (color_string, &color))
- return;
-
- sprintf (
- color_string, "#%02x%02x%02x",
- (gint) color.red * 256 / 65536,
- (gint) color.green * 256 / 65536,
- (gint) color.blue * 256 / 65536);
-}
-
-static void
-settings_web_view_gtkhtml_load_style (ESettingsWebViewGtkHTML *extension)
-{
- GString *buffer;
- gchar *citation_color;
- gchar *monospace_font;
- gchar *spell_color;
- gchar *variable_font;
- gboolean custom_fonts;
- gboolean mark_citations;
- EExtensible *extensible;
- GtkStyleContext *style_context;
- GSettings *settings;
- GError *error = NULL;
-
- /* Some of our mail and composer preferences are passed down to
- * GtkHtml through style properties, unfortunately. This builds
- * a style sheet for the EWebView using values from GSettings. */
-
- settings = extension->priv->settings;
-
- custom_fonts =
- g_settings_get_boolean (settings, "use-custom-font");
- monospace_font =
- g_settings_get_string (settings, "monospace-font");
- variable_font =
- g_settings_get_string (settings, "variable-width-font");
- mark_citations =
- g_settings_get_boolean (settings, "mark-citations");
- citation_color =
- g_settings_get_string (settings, "citation-color");
- spell_color =
- g_settings_get_string (settings, "composer-spell-color");
-
- buffer = g_string_new ("EWebViewGtkHTML {\n");
-
- settings_web_view_gtkhtml_fix_color_string (citation_color);
- settings_web_view_gtkhtml_fix_color_string (spell_color);
-
- if (custom_fonts && variable_font != NULL)
- g_string_append_printf (
- buffer, " font: %s;\n", variable_font);
-
- if (custom_fonts && monospace_font != NULL)
- g_string_append_printf (
- buffer, " -GtkHTML-fixed-font-name: '%s';\n",
- monospace_font);
-
- if (mark_citations && citation_color != NULL)
- g_string_append_printf (
- buffer, " -GtkHTML-cite-color: %s;\n",
- citation_color);
-
- if (spell_color != NULL)
- g_string_append_printf (
- buffer, " -GtkHTML-spell-error-color: %s;\n",
- spell_color);
-
- g_string_append (buffer, "}\n");
-
- gtk_css_provider_load_from_data (
- extension->priv->css_provider,
- buffer->str, buffer->len, &error);
-
- if (error != NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
-
- g_string_free (buffer, TRUE);
-
- g_free (monospace_font);
- g_free (variable_font);
- g_free (citation_color);
- g_free (spell_color);
-
- extensible = e_extension_get_extensible (E_EXTENSION (extension));
- style_context = gtk_widget_get_style_context (GTK_WIDGET (extensible));
- gtk_style_context_invalidate (style_context);
-}
-
-static void
-settings_web_view_gtkhtml_changed_cb (GSettings *settings,
- const gchar *key,
- ESettingsWebViewGtkHTML *extension)
-{
- GVariant *new_value, *old_value;
-
- new_value = g_settings_get_value (settings, key);
- old_value = g_hash_table_lookup (extension->priv->old_values, key);
-
- if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
- if (new_value)
- g_hash_table_insert (extension->priv->old_values, g_strdup (key), new_value);
- else
- g_hash_table_remove (extension->priv->old_values, key);
-
- settings_web_view_gtkhtml_load_style (extension);
- } else if (new_value) {
- g_variant_unref (new_value);
- }
-}
-
-static void
-settings_web_view_gtkhtml_realize (GtkWidget *widget,
- ESettingsWebViewGtkHTML *extension)
-{
- GSettings *settings;
-
- settings = extension->priv->settings;
-
- g_settings_bind (
- settings, "composer-inline-spelling",
- widget, "inline-spelling",
- G_SETTINGS_BIND_GET);
-
- g_settings_bind (
- settings, "composer-magic-links",
- widget, "magic-links",
- G_SETTINGS_BIND_GET);
-
- g_settings_bind (
- settings, "composer-magic-smileys",
- widget, "magic-smileys",
- G_SETTINGS_BIND_GET);
-
- gtk_style_context_add_provider (
- gtk_widget_get_style_context (widget),
- GTK_STYLE_PROVIDER (extension->priv->css_provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
- settings_web_view_gtkhtml_load_style (extension);
-
- /* Reload the style sheet when certain settings change. */
-
- g_signal_connect (
- settings, "changed::use-custom-font",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-
- g_signal_connect (
- settings, "changed::monospace-font",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-
- g_signal_connect (
- settings, "changed::variable-width-font",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-
- g_signal_connect (
- settings, "changed::mark-citations",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-
- g_signal_connect (
- settings, "changed::citation-color",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-
- g_signal_connect (
- settings, "changed::composer-spell-color",
- G_CALLBACK (settings_web_view_gtkhtml_changed_cb),
- extension);
-}
-
-static void
-settings_web_view_gtkhtml_dispose (GObject *object)
-{
- ESettingsWebViewGtkHTMLPrivate *priv;
-
- priv = E_SETTINGS_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
-
- if (priv->settings != NULL) {
- g_signal_handlers_disconnect_by_func (
- priv->settings,
- settings_web_view_gtkhtml_changed_cb, object);
- }
-
- if (priv->old_values) {
- g_hash_table_destroy (priv->old_values);
- priv->old_values = NULL;
- }
-
- g_clear_object (&priv->css_provider);
- g_clear_object (&priv->settings);
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_settings_web_view_gtkhtml_parent_class)->
- dispose (object);
-}
-
-static void
-settings_web_view_gtkhtml_constructed (GObject *object)
-{
- EExtensible *extensible;
-
- extensible = e_extension_get_extensible (E_EXTENSION (object));
-
- /* Wait to bind settings until the EWebView is realized so
- * GtkhtmlEditor has a chance to install a GtkHTMLEditorAPI.
- * Otherwise our settings will have no effect. */
-
- g_signal_connect (
- extensible, "realize",
- G_CALLBACK (settings_web_view_gtkhtml_realize), object);
-
- /* Chain up to parent's constructed() method. */
- G_OBJECT_CLASS (e_settings_web_view_gtkhtml_parent_class)->
- constructed (object);
-}
-
-static void
-e_settings_web_view_gtkhtml_class_init (ESettingsWebViewGtkHTMLClass *class)
-{
- GObjectClass *object_class;
- EExtensionClass *extension_class;
-
- g_type_class_add_private (
- class, sizeof (ESettingsWebViewGtkHTMLPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->dispose = settings_web_view_gtkhtml_dispose;
- object_class->constructed = settings_web_view_gtkhtml_constructed;
-
- extension_class = E_EXTENSION_CLASS (class);
- extension_class->extensible_type = E_TYPE_WEB_VIEW_GTKHTML;
-}
-
-static void
-e_settings_web_view_gtkhtml_class_finalize (ESettingsWebViewGtkHTMLClass *class)
-{
-}
-
-static void
-e_settings_web_view_gtkhtml_init (ESettingsWebViewGtkHTML *extension)
-{
- GSettings *settings;
-
- extension->priv =
- E_SETTINGS_WEB_VIEW_GTKHTML_GET_PRIVATE (extension);
-
- extension->priv->css_provider = gtk_css_provider_new ();
-
- settings = g_settings_new ("org.gnome.evolution.mail");
- extension->priv->settings = settings;
-
- extension->priv->old_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
-}
-
-void
-e_settings_web_view_gtkhtml_type_register (GTypeModule *type_module)
-{
- /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
- * function, so we have to wrap it with a public function in
- * order to register types from a separate compilation unit. */
- e_settings_web_view_gtkhtml_register_type (type_module);
-}
-
diff --git a/modules/settings/e-settings-web-view-gtkhtml.h b/modules/settings/e-settings-web-view-gtkhtml.h
deleted file mode 100644
index 7c406265e4..0000000000
--- a/modules/settings/e-settings-web-view-gtkhtml.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * e-settings-web-view-gtkhtml.h
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef E_SETTINGS_WEB_VIEW_GTKHTML_H
-#define E_SETTINGS_WEB_VIEW_GTKHTML_H
-
-#include <libebackend/libebackend.h>
-
-/* Standard GObject macros */
-#define E_TYPE_SETTINGS_WEB_VIEW_GTKHTML \
- (e_settings_web_view_gtkhtml_get_type ())
-#define E_SETTINGS_WEB_VIEW_GTKHTML(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML, ESettingsWebViewGtkHTML))
-#define E_SETTINGS_WEB_VIEW_GTKHTML_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML, ESettingsWebViewGtkHTMLClass))
-#define E_IS_SETTINGS_WEB_VIEW_GTKHTML(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML))
-#define E_IS_SETTINGS_WEB_VIEW_GTKHTML_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML))
-#define E_SETTINGS_WEB_VIEW_GTKHTML_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_SETTINGS_WEB_VIEW_GTKHTML, ESettingsWebViewGtkHTMLClass))
-
-G_BEGIN_DECLS
-
-typedef struct _ESettingsWebViewGtkHTML ESettingsWebViewGtkHTML;
-typedef struct _ESettingsWebViewGtkHTMLClass ESettingsWebViewGtkHTMLClass;
-typedef struct _ESettingsWebViewGtkHTMLPrivate ESettingsWebViewGtkHTMLPrivate;
-
-struct _ESettingsWebViewGtkHTML {
- EExtension parent;
- ESettingsWebViewGtkHTMLPrivate *priv;
-};
-
-struct _ESettingsWebViewGtkHTMLClass {
- EExtensionClass parent_class;
-};
-
-GType e_settings_web_view_gtkhtml_get_type
- (void) G_GNUC_CONST;
-void e_settings_web_view_gtkhtml_type_register
- (GTypeModule *type_module);
-
-G_END_DECLS
-
-#endif /* E_SETTINGS_WEB_VIEW_GTKHTML_H */
diff --git a/modules/settings/e-settings-web-view.c b/modules/settings/e-settings-web-view.c
index 0867b3c66d..ff804f36b4 100644
--- a/modules/settings/e-settings-web-view.c
+++ b/modules/settings/e-settings-web-view.c
@@ -31,7 +31,8 @@
((obj), E_TYPE_SETTINGS_WEB_VIEW, ESettingsWebViewPrivate))
struct _ESettingsWebViewPrivate {
- gint placeholder;
+ GtkCssProvider *css_provider;
+ GSettings *settings;
};
G_DEFINE_DYNAMIC_TYPE (
@@ -39,32 +40,194 @@ G_DEFINE_DYNAMIC_TYPE (
e_settings_web_view,
E_TYPE_EXTENSION)
+/* replaces content of color string */
static void
-settings_web_view_constructed (GObject *object)
+settings_web_view_fix_color_string (gchar *color_string)
{
- GSettings *settings;
+ GdkColor color;
+
+ if (color_string == NULL)
+ return;
+
+ if (strlen (color_string) < 13)
+ return;
+
+ if (!gdk_color_parse (color_string, &color))
+ return;
+
+ sprintf (
+ color_string, "#%02x%02x%02x",
+ (gint) color.red * 256 / 65536,
+ (gint) color.green * 256 / 65536,
+ (gint) color.blue * 256 / 65536);
+}
+
+static void
+settings_web_view_load_style (ESettingsWebView *extension)
+{
+ GString *buffer;
+ gchar *citation_color;
+ gchar *monospace_font;
+ gchar *variable_font;
+ gboolean custom_fonts;
+ gboolean mark_citations;
EExtensible *extensible;
+ GtkStyleContext *style_context;
+ GSettings *settings;
+ GError *error = NULL;
- extensible = e_extension_get_extensible (E_EXTENSION (object));
+ /* Some of our mail and composer preferences are passed down to
+ * GtkHtml through style properties, unfortunately. This builds
+ * a style sheet for the EWebView using values from GSettings. */
- settings = g_settings_new ("org.gnome.evolution.mail");
+ settings = extension->priv->settings;
+
+ custom_fonts =
+ g_settings_get_boolean (settings, "use-custom-font");
+ monospace_font =
+ g_settings_get_string (settings, "monospace-font");
+ variable_font =
+ g_settings_get_string (settings, "variable-width-font");
+ mark_citations =
+ g_settings_get_boolean (settings, "mark-citations");
+ citation_color =
+ g_settings_get_string (settings, "citation-color");
+
+ buffer = g_string_new ("EWebViewGtkHTML {\n");
+
+ settings_web_view_fix_color_string (citation_color);
+
+ if (custom_fonts && variable_font != NULL)
+ g_string_append_printf (
+ buffer, " font: %s;\n", variable_font);
+
+ if (custom_fonts && monospace_font != NULL)
+ g_string_append_printf (
+ buffer, " -GtkHTML-fixed-font-name: '%s';\n",
+ monospace_font);
+
+ if (mark_citations && citation_color != NULL)
+ g_string_append_printf (
+ buffer, " -GtkHTML-cite-color: %s;\n",
+ citation_color);
+
+ g_string_append (buffer, "}\n");
+
+ gtk_css_provider_load_from_data (
+ extension->priv->css_provider,
+ buffer->str, buffer->len, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_string_free (buffer, TRUE);
+
+ g_free (monospace_font);
+ g_free (variable_font);
+ g_free (citation_color);
+
+ extensible = e_extension_get_extensible (E_EXTENSION (extension));
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (extensible));
+ gtk_style_context_invalidate (style_context);
+}
+
+static void
+settings_web_view_changed_cb (GSettings *settings,
+ const gchar *key,
+ ESettingsWebView *extension)
+{
+ settings_web_view_load_style (extension);
+}
+
+static void
+settings_web_view_realize (GtkWidget *widget,
+ ESettingsWebView *extension)
+{
+ GSettings *settings;
+
+ settings = extension->priv->settings;
g_settings_bind (
settings, "composer-inline-spelling",
- extensible, "inline-spelling",
+ widget, "inline-spelling",
G_SETTINGS_BIND_GET);
g_settings_bind (
settings, "composer-magic-links",
- extensible, "magic-links",
+ widget, "magic-links",
G_SETTINGS_BIND_GET);
g_settings_bind (
settings, "composer-magic-smileys",
- extensible, "magic-smileys",
+ widget, "magic-smileys",
G_SETTINGS_BIND_GET);
- g_object_unref (settings);
+ gtk_style_context_add_provider (
+ gtk_widget_get_style_context (widget),
+ GTK_STYLE_PROVIDER (extension->priv->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ settings_web_view_load_style (extension);
+
+ /* Reload the style sheet when certain settings change. */
+
+ g_signal_connect (
+ settings, "changed::use-custom-font",
+ G_CALLBACK (settings_web_view_changed_cb), extension);
+
+ g_signal_connect (
+ settings, "changed::monospace-font",
+ G_CALLBACK (settings_web_view_changed_cb), extension);
+
+ g_signal_connect (
+ settings, "changed::variable-width-font",
+ G_CALLBACK (settings_web_view_changed_cb), extension);
+
+ g_signal_connect (
+ settings, "changed::mark-citations",
+ G_CALLBACK (settings_web_view_changed_cb), extension);
+
+ g_signal_connect (
+ settings, "changed::citation-color",
+ G_CALLBACK (settings_web_view_changed_cb), extension);
+}
+
+static void
+settings_web_view_dispose (GObject *object)
+{
+ ESettingsWebViewPrivate *priv;
+
+ priv = E_SETTINGS_WEB_VIEW_GET_PRIVATE (object);
+
+ if (priv->settings != NULL) {
+ g_signal_handlers_disconnect_by_func (
+ priv->settings,
+ settings_web_view_changed_cb, object);
+ }
+
+ g_clear_object (&priv->css_provider);
+ g_clear_object (&priv->settings);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_settings_web_view_parent_class)->dispose (object);
+}
+
+static void
+settings_web_view_constructed (GObject *object)
+{
+ EExtensible *extensible;
+
+ extensible = e_extension_get_extensible (E_EXTENSION (object));
+
+ /* Wait to bind settings until the EWebView is realized so
+ * GtkhtmlEditor has a chance to install a GtkHTMLEditorAPI.
+ * Otherwise our settings will have no effect. */
+
+ g_signal_connect (
+ extensible, "realize",
+ G_CALLBACK (settings_web_view_realize), object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_settings_web_view_parent_class)->
@@ -80,6 +243,7 @@ e_settings_web_view_class_init (ESettingsWebViewClass *class)
g_type_class_add_private (class, sizeof (ESettingsWebViewPrivate));
object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = settings_web_view_dispose;
object_class->constructed = settings_web_view_constructed;
extension_class = E_EXTENSION_CLASS (class);
@@ -94,7 +258,14 @@ e_settings_web_view_class_finalize (ESettingsWebViewClass *class)
static void
e_settings_web_view_init (ESettingsWebView *extension)
{
+ GSettings *settings;
+
extension->priv = E_SETTINGS_WEB_VIEW_GET_PRIVATE (extension);
+
+ extension->priv->css_provider = gtk_css_provider_new ();
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ extension->priv->settings = settings;
}
void
diff --git a/modules/settings/evolution-module-settings.c b/modules/settings/evolution-module-settings.c
index af08158dde..88101b56c1 100644
--- a/modules/settings/evolution-module-settings.c
+++ b/modules/settings/evolution-module-settings.c
@@ -31,9 +31,9 @@
#include "e-settings-meeting-time-selector.h"
#include "e-settings-message-list.h"
#include "e-settings-name-selector-entry.h"
+#include "e-settings-spell-checker.h"
#include "e-settings-spell-entry.h"
#include "e-settings-web-view.h"
-#include "e-settings-web-view-gtkhtml.h"
#include "e-settings-weekday-chooser.h"
/* Module Entry Points */
@@ -59,9 +59,9 @@ e_module_load (GTypeModule *type_module)
e_settings_meeting_time_selector_type_register (type_module);
e_settings_message_list_type_register (type_module);
e_settings_name_selector_entry_type_register (type_module);
+ e_settings_spell_checker_type_register (type_module);
e_settings_spell_entry_type_register (type_module);
e_settings_web_view_type_register (type_module);
- e_settings_web_view_gtkhtml_type_register (type_module);
e_settings_weekday_chooser_type_register (type_module);
}
diff --git a/modules/spamassassin/Makefile.am b/modules/spamassassin/Makefile.am
index 2ecb217f2c..ae5c32dd8b 100644
--- a/modules/spamassassin/Makefile.am
+++ b/modules/spamassassin/Makefile.am
@@ -6,8 +6,8 @@ module_spamassassin_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-spamassassin\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_spamassassin_la_SOURCES = \
evolution-spamassassin.c
@@ -19,7 +19,7 @@ module_spamassassin_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_spamassassin_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/startup-wizard/Makefile.am b/modules/startup-wizard/Makefile.am
index 68b08f68e2..4bf9971355 100644
--- a/modules/startup-wizard/Makefile.am
+++ b/modules/startup-wizard/Makefile.am
@@ -8,7 +8,6 @@ module_startup_wizard_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-startup-wizard\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
@@ -31,7 +30,6 @@ module_startup_wizard_la_LIBADD = \
$(libevolution_mail_settings_la) \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
$(NULL)
module_startup_wizard_la_LDFLAGS = \
diff --git a/modules/text-highlight/Makefile.am b/modules/text-highlight/Makefile.am
index f105ff78af..ab5df6793c 100644
--- a/modules/text-highlight/Makefile.am
+++ b/modules/text-highlight/Makefile.am
@@ -7,8 +7,8 @@ module_text_highlight_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-text-highlight\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_text_highlight_la_SOURCES = \
e-mail-display-popup-text-highlight.c \
@@ -28,7 +28,7 @@ module_text_highlight_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_text_highlight_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/tnef-attachment/Makefile.am b/modules/tnef-attachment/Makefile.am
index 09ff602c99..4ba29db2f7 100644
--- a/modules/tnef-attachment/Makefile.am
+++ b/modules/tnef-attachment/Makefile.am
@@ -13,9 +13,9 @@ module_tnef_attachment_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-tnef-attachment\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(TNEF_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_tnef_attachment_la_SOURCES = \
e-mail-parser-tnef-attachment.c \
@@ -27,8 +27,8 @@ module_tnef_attachment_la_LIBADD = \
$(top_builddir)/em-format/libevolution-mail-formatter.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- -lytnef
+ -lytnef \
+ $(NULL)
module_tnef_attachment_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/vcard-inline/Makefile.am b/modules/vcard-inline/Makefile.am
index 22af723783..5f5a6e50f6 100644
--- a/modules/vcard-inline/Makefile.am
+++ b/modules/vcard-inline/Makefile.am
@@ -7,8 +7,8 @@ module_vcard_inline_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-module-vcard-inline\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CODE_COVERAGE_CFLAGS)
+ $(NULL)
module_vcard_inline_la_SOURCES = \
e-mail-formatter-vcard.c \
@@ -27,7 +27,7 @@ module_vcard_inline_la_LIBADD = \
$(top_builddir)/addressbook/printing/libecontactprint.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_vcard_inline_la_LDFLAGS = \
-avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/web-inspector/Makefile.am b/modules/web-inspector/Makefile.am
index 43615d549b..237121e237 100644
--- a/modules/web-inspector/Makefile.am
+++ b/modules/web-inspector/Makefile.am
@@ -6,8 +6,8 @@ module_web_inspector_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-web-inspector\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
module_web_inspector_la_SOURCES = \
evolution-web-inspector.c
@@ -16,7 +16,7 @@ module_web_inspector_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
module_web_inspector_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/plugins/attachment-reminder/Makefile.am b/plugins/attachment-reminder/Makefile.am
index 9fbbdb1271..023dab3ccd 100644
--- a/plugins/attachment-reminder/Makefile.am
+++ b/plugins/attachment-reminder/Makefile.am
@@ -16,8 +16,8 @@ liborg_gnome_evolution_attachment_reminder_la_CPPFLAGS = \
-DEVOLUTION_PLUGINDIR="\"$(plugindir)\"" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_evolution_attachment_reminder_la_SOURCES = attachment-reminder.c
@@ -32,7 +32,7 @@ liborg_gnome_evolution_attachment_reminder_la_LIBADD = \
$(top_builddir)/mail/libevolution-mail.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-evolution-attachment-reminder.eplug.xml \
org-gnome-attachment-reminder.error.xml
diff --git a/plugins/bbdb/Makefile.am b/plugins/bbdb/Makefile.am
index 991f662cbc..48d8796712 100644
--- a/plugins/bbdb/Makefile.am
+++ b/plugins/bbdb/Makefile.am
@@ -17,8 +17,8 @@ liborg_gnome_evolution_bbdb_la_CPPFLAGS = \
-I$(top_srcdir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_evolution_bbdb_la_SOURCES = bbdb.c bbdb.h gaimbuddies.c
@@ -32,7 +32,7 @@ liborg_gnome_evolution_bbdb_la_LIBADD = \
$(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-evolution-bbdb.eplug.xml
diff --git a/plugins/dbx-import/Makefile.am b/plugins/dbx-import/Makefile.am
index 80c5f9f953..736faf60da 100644
--- a/plugins/dbx-import/Makefile.am
+++ b/plugins/dbx-import/Makefile.am
@@ -16,8 +16,8 @@ liborg_gnome_dbx_import_la_CPPFLAGS = \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_dbx_import_la_SOURCES = dbx-importer.c
@@ -30,7 +30,7 @@ liborg_gnome_dbx_import_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-dbx-import.eplug.xml
diff --git a/plugins/email-custom-header/Makefile.am b/plugins/email-custom-header/Makefile.am
index 7014929882..04d0536ba3 100644
--- a/plugins/email-custom-header/Makefile.am
+++ b/plugins/email-custom-header/Makefile.am
@@ -12,8 +12,8 @@ liborg_gnome_email_custom_header_la_CPPFLAGS = \
-I$(top_builddir)/composer \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_email_custom_header_la_SOURCES = \
email-custom-header.c \
@@ -25,7 +25,7 @@ liborg_gnome_email_custom_header_la_LIBADD = \
$(top_builddir)/mail/libevolution-mail.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
liborg_gnome_email_custom_header_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/plugins/email-custom-header/email-custom-header.c b/plugins/email-custom-header/email-custom-header.c
index d933e1ae82..eed2954857 100644
--- a/plugins/email-custom-header/email-custom-header.c
+++ b/plugins/email-custom-header/email-custom-header.c
@@ -495,15 +495,16 @@ destroy_compo_data (gpointer data)
static void
action_email_custom_header_cb (GtkAction *action,
EMsgComposer *composer)
-
{
GtkUIManager *ui_manager;
GtkWidget *menuitem;
GdkWindow *window;
CustomHeaderOptionsDialog *dialog = NULL;
EmailCustomHeaderWindow *new_email_custom_header_window = NULL;
+ EHTMLEditor *editor;
- 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);
menuitem = gtk_ui_manager_get_widget (ui_manager, "/main-menu/insert-menu/insert-menu-top/Custom Header");
new_email_custom_header_window = g_object_get_data ((GObject *) composer, "compowindow");
@@ -545,13 +546,13 @@ gboolean
e_plugin_ui_init (GtkUIManager *ui_manager,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
/* Add actions to the "composer" action group. */
gtk_action_group_add_actions (
- gtkhtml_editor_get_action_group (editor, "composer"),
+ e_html_editor_get_action_group (editor, "composer"),
entries, G_N_ELEMENTS (entries), composer);
return TRUE;
diff --git a/plugins/external-editor/Makefile.am b/plugins/external-editor/Makefile.am
index 98c547e52f..8d325be0fc 100644
--- a/plugins/external-editor/Makefile.am
+++ b/plugins/external-editor/Makefile.am
@@ -28,8 +28,8 @@ liborg_gnome_external_editor_la_CPPFLAGS = \
-I$(top_srcdir)/composer \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_external_editor_la_SOURCES = \
external-editor.c
@@ -46,7 +46,7 @@ liborg_gnome_external_editor_la_LIBADD = \
$(top_builddir)/mail/libevolution-mail.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = \
org-gnome-external-editor.eplug.xml \
diff --git a/plugins/external-editor/external-editor.c b/plugins/external-editor/external-editor.c
index 584c0b61e4..2eb00b6e87 100644
--- a/plugins/external-editor/external-editor.c
+++ b/plugins/external-editor/external-editor.c
@@ -149,29 +149,28 @@ static void
enable_disable_composer (EMsgComposer *composer,
gboolean enable)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
GtkAction *action;
GtkActionGroup *action_group;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
- if (enable)
- gtkhtml_editor_run_command (editor, "editable-on");
- else
- gtkhtml_editor_run_command (editor, "editable-off");
+ webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), enable);
- action = GTKHTML_EDITOR_ACTION_EDIT_MENU (composer);
+ action = E_HTML_EDITOR_ACTION_EDIT_MENU (editor);
gtk_action_set_sensitive (action, enable);
- action = GTKHTML_EDITOR_ACTION_FORMAT_MENU (composer);
+ action = E_HTML_EDITOR_ACTION_FORMAT_MENU (editor);
gtk_action_set_sensitive (action, enable);
- action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer);
+ action = E_HTML_EDITOR_ACTION_INSERT_MENU (editor);
gtk_action_set_sensitive (action, enable);
- action_group = gtkhtml_editor_get_action_group (editor, "composer");
+ action_group = e_html_editor_get_action_group (editor, "composer");
gtk_action_group_set_sensitive (action_group, enable);
}
@@ -192,16 +191,21 @@ static gboolean
update_composer_text (GArray *array)
{
EMsgComposer *composer;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
gchar *text;
composer = g_array_index (array, gpointer, 0);
text = g_array_index (array, gpointer, 1);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
+
e_msg_composer_set_body_text (composer, text, FALSE);
enable_composer (composer);
- gtkhtml_editor_set_changed (GTKHTML_EDITOR (composer), TRUE);
+ e_html_editor_view_set_changed (view, TRUE);
g_free (text);
@@ -250,6 +254,49 @@ numlines (const gchar *text,
return lineno;
}
+static gint
+get_caret_position (EHTMLEditorView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *selection;
+ WebKitDOMRange *range;
+ gint range_count;
+ WebKitDOMNodeList *nodes;
+ gulong ii, length;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+ window = webkit_dom_document_get_default_view (document);
+ selection = webkit_dom_dom_window_get_selection (window);
+
+ if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+ return 0;
+
+ range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+ range_count = 0;
+ nodes = webkit_dom_node_get_child_nodes (
+ webkit_dom_node_get_parent_node (
+ webkit_dom_dom_selection_get_anchor_node (
+ selection)));
+ length = webkit_dom_node_list_get_length (nodes);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node;
+
+ node = webkit_dom_node_list_item (nodes, ii);
+ if (webkit_dom_node_is_same_node (
+ node, webkit_dom_dom_selection_get_anchor_node (selection))) {
+
+ break;
+ } else if (webkit_dom_node_get_node_type (node) == 3) {
+ gchar *text = webkit_dom_node_get_text_content (node);
+ range_count += strlen (text);
+ g_free (text);
+ }
+ }
+
+ return webkit_dom_range_get_start_offset (range, NULL) + range_count;
+}
+
static gboolean external_editor_running = FALSE;
static GMutex external_editor_running_lock;
@@ -262,18 +309,21 @@ external_editor_thread (gpointer user_data)
GSettings *settings;
gchar *editor_cmd_line = NULL, *editor_cmd = NULL, *content;
gint fd, position = -1, offset = -1;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
+
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
/* prefix temp files with evo so .*vimrc can be setup to recognize them */
fd = g_file_open_tmp ("evoXXXXXX", &filename, NULL);
if (fd > 0) {
- gsize length = 0;
-
close (fd);
d (printf ("\n\aTemporary-file Name is : [%s] \n\a", filename));
/* Push the text (if there is one) from the composer to the file */
- content = gtkhtml_editor_get_text_plain (GTKHTML_EDITOR (composer), &length);
- g_file_set_contents (filename, content, length, NULL);
+ content = e_html_editor_view_get_text_plain (view);
+ g_file_set_contents (filename, content, strlen (content), NULL);
} else {
struct run_error_dialog_data *data;
@@ -292,7 +342,7 @@ external_editor_thread (gpointer user_data)
settings = g_settings_new ("org.gnome.evolution.plugin.external-editor");
editor_cmd = g_settings_get_string (settings, "command");
if (!editor_cmd) {
- if (!(editor_cmd = g_strdup (g_getenv ("EDITOR"))))
+ if (!(editor_cmd = g_strdup (g_getenv ("EDITOR"))) )
/* Make gedit the default external editor,
* if the default schemas are not installed
* and no $EDITOR is set. */
@@ -300,11 +350,8 @@ external_editor_thread (gpointer user_data)
}
g_object_unref (settings);
- if (g_strrstr (editor_cmd, "vim") != NULL
- && gtk_html_get_cursor_pos (
- gtkhtml_editor_get_html (
- GTKHTML_EDITOR (composer)), &position, &offset)
- && position >= 0 && offset >= 0) {
+ if (g_strrstr (editor_cmd, "vim") != NULL &&
+ ((position = get_caret_position (view)) > 0)) {
gchar *tmp = editor_cmd;
gint lineno;
gboolean set_nofork;
@@ -385,7 +432,7 @@ external_editor_thread (gpointer user_data)
}
}
- finished:
+finished:
g_mutex_lock (&external_editor_running_lock);
external_editor_running = FALSE;
g_mutex_unlock (&external_editor_running_lock);
@@ -484,24 +531,23 @@ gboolean
e_plugin_ui_init (GtkUIManager *manager,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
- EWebViewGtkHTML *web_view;
+ EHTMLEditor *editor;
+ EHTMLEditorView *view;
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
+ view = e_html_editor_get_view (editor);
/* Add actions to the "composer" action group. */
gtk_action_group_add_actions (
- gtkhtml_editor_get_action_group (editor, "composer"),
+ e_html_editor_get_action_group (editor, "composer"),
entries, G_N_ELEMENTS (entries), composer);
- web_view = e_msg_composer_get_web_view (composer);
-
g_signal_connect (
- web_view, "key_press_event",
+ view, "key_press_event",
G_CALLBACK (key_press_cb), composer);
g_signal_connect (
- web_view, "delete-event",
+ view, "delete-event",
G_CALLBACK (delete_cb), composer);
return TRUE;
diff --git a/plugins/face/Makefile.am b/plugins/face/Makefile.am
index dd449378c6..1fd5909789 100644
--- a/plugins/face/Makefile.am
+++ b/plugins/face/Makefile.am
@@ -12,8 +12,8 @@ liborg_gnome_face_la_CPPFLAGS = \
-I$(top_builddir)/composer \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_face_la_SOURCES = face.c
@@ -23,7 +23,7 @@ liborg_gnome_face_la_LIBADD = \
$(top_builddir)/mail/libevolution-mail.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
liborg_gnome_face_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/plugins/face/face.c b/plugins/face/face.c
index e40bdfc16a..cfcdb968b0 100644
--- a/plugins/face/face.c
+++ b/plugins/face/face.c
@@ -423,7 +423,7 @@ gboolean
e_plugin_ui_init (GtkUIManager *ui_manager,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
static GtkToggleActionEntry entries[] = {
{ "face-plugin",
@@ -444,11 +444,11 @@ e_plugin_ui_init (GtkUIManager *ui_manager,
g_free (face);
}
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
/* Add actions to the "composer" action group. */
gtk_action_group_add_toggle_actions (
- gtkhtml_editor_get_action_group (editor, "composer"),
+ e_html_editor_get_action_group (editor, "composer"),
entries, G_N_ELEMENTS (entries), composer);
return TRUE;
@@ -464,11 +464,11 @@ void
face_handle_send (EPlugin *ep,
EMEventTargetComposer *target)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
GtkAction *action;
- editor = GTKHTML_EDITOR (target->composer);
- action = gtkhtml_editor_get_action (editor, "face-plugin");
+ editor = e_msg_composer_get_editor (target->composer);
+ action = e_html_editor_get_action (editor, "face-plugin");
g_return_if_fail (action != NULL);
diff --git a/plugins/mail-notification/Makefile.am b/plugins/mail-notification/Makefile.am
index 521690cde0..cef5c9aac0 100644
--- a/plugins/mail-notification/Makefile.am
+++ b/plugins/mail-notification/Makefile.am
@@ -18,8 +18,8 @@ liborg_gnome_mail_notification_la_CPPFLAGS = \
$(GNOME_PLATFORM_CFLAGS) \
$(LIBNOTIFY_CFLAGS) \
$(CANBERRA_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_mail_notification_la_SOURCES = mail-notification.c
@@ -34,7 +34,7 @@ liborg_gnome_mail_notification_la_LIBADD = \
$(GNOME_PLATFORM_LIBS) \
$(LIBNOTIFY_LIBS) \
$(CANBERRA_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
BUILT_SOURCES = $(plugin_DATA)
diff --git a/plugins/mail-to-task/Makefile.am b/plugins/mail-to-task/Makefile.am
index fd4c4c5db9..1d2d925b74 100644
--- a/plugins/mail-to-task/Makefile.am
+++ b/plugins/mail-to-task/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_mail_to_task_la_CPPFLAGS = \
-I$(top_srcdir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_mail_to_task_la_SOURCES = mail-to-task.c
@@ -25,7 +25,7 @@ liborg_gnome_mail_to_task_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-mail-to-task.eplug.xml
diff --git a/plugins/mailing-list-actions/Makefile.am b/plugins/mailing-list-actions/Makefile.am
index fcb9f55f72..cc6e28ca4f 100644
--- a/plugins/mailing-list-actions/Makefile.am
+++ b/plugins/mailing-list-actions/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_mailing_list_actions_la_CPPFLAGS = \
-I$(top_builddir)/composer \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_mailing_list_actions_la_SOURCES = mailing-list-actions.c
@@ -24,7 +24,7 @@ liborg_gnome_mailing_list_actions_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
error_DATA = org-gnome-mailing-list-actions.error
errordir = $(privdatadir)/errors
diff --git a/plugins/pst-import/Makefile.am b/plugins/pst-import/Makefile.am
index 6e6d89a1b3..755edf8a3e 100644
--- a/plugins/pst-import/Makefile.am
+++ b/plugins/pst-import/Makefile.am
@@ -17,9 +17,9 @@ liborg_gnome_pst_import_la_CPPFLAGS = \
-I$(top_builddir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBPST_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_pst_import_la_SOURCES = pst-importer.c
@@ -32,8 +32,8 @@ liborg_gnome_pst_import_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(LIBPST_LIBS)
+ $(LIBPST_LIBS) \
+ $(NULL)
EXTRA_DIST = org-gnome-pst-import.eplug.xml
diff --git a/plugins/publish-calendar/Makefile.am b/plugins/publish-calendar/Makefile.am
index a24b979792..a34dbd7a04 100644
--- a/plugins/publish-calendar/Makefile.am
+++ b/plugins/publish-calendar/Makefile.am
@@ -16,9 +16,9 @@ liborg_gnome_publish_calendar_la_CPPFLAGS = \
-DEVOLUTION_UIDIR=\""$(uidir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(LIBNOTIFY_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_publish_calendar_la_SOURCES = \
publish-calendar.c \
@@ -39,8 +39,8 @@ liborg_gnome_publish_calendar_la_LIBADD = \
$(top_builddir)/calendar/gui/libevolution-calendar.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(LIBNOTIFY_LIBS)
+ $(LIBNOTIFY_LIBS) \
+ $(NULL)
EXTRA_DIST = \
org-gnome-publish-calendar.eplug.xml \
diff --git a/plugins/save-calendar/Makefile.am b/plugins/save-calendar/Makefile.am
index 2a70531cd8..eb3bd54e5f 100644
--- a/plugins/save-calendar/Makefile.am
+++ b/plugins/save-calendar/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_save_calendar_la_CPPFLAGS = \
-I$(top_srcdir) \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_save_calendar_la_SOURCES = \
save-calendar.c \
@@ -26,7 +26,7 @@ liborg_gnome_save_calendar_la_LIBADD = \
$(top_builddir)/shell/libevolution-shell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-save-calendar.eplug.xml
diff --git a/plugins/templates/Makefile.am b/plugins/templates/Makefile.am
index a5d56c7958..18d25ea5e7 100644
--- a/plugins/templates/Makefile.am
+++ b/plugins/templates/Makefile.am
@@ -11,8 +11,8 @@ liborg_gnome_templates_la_CPPFLAGS = \
-I$(top_builddir)/composer \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
liborg_gnome_templates_la_SOURCES = templates.c
@@ -26,7 +26,7 @@ liborg_gnome_templates_la_LIBADD = \
$(top_builddir)/libemail-engine/libemail-engine.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS)
+ $(NULL)
EXTRA_DIST = org-gnome-templates.eplug.xml
diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c
index 0a50178044..e3883a1fbe 100644
--- a/plugins/templates/templates.c
+++ b/plugins/templates/templates.c
@@ -1385,13 +1385,13 @@ gboolean
init_composer_actions (GtkUIManager *ui_manager,
EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ EHTMLEditor *editor;
- editor = GTKHTML_EDITOR (composer);
+ editor = e_msg_composer_get_editor (composer);
/* Add actions to the "composer" action group. */
gtk_action_group_add_actions (
- gtkhtml_editor_get_action_group (editor, "composer"),
+ e_html_editor_get_action_group (editor, "composer"),
composer_entries, G_N_ELEMENTS (composer_entries), composer);
return TRUE;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 62d6c6a41d..36c896fc59 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -212,9 +212,12 @@ e-util/e-cell-text.c
e-util/e-charset-combo-box.c
e-util/e-charset.c
e-util/e-client-cache.c
+e-util/e-color-chooser-widget.c
+e-util/e-color-combo.c
e-util/e-dateedit.c
e-util/e-datetime-format.c
e-util/e-dialog-utils.c
+e-util/e-emoticon-chooser.c
e-util/e-file-utils.c
e-util/e-filter-datespec.c
e-util/e-filter-file.c
@@ -223,6 +226,20 @@ e-util/e-filter-option.c
e-util/e-filter-part.c
e-util/e-filter-rule.c
e-util/e-focus-tracker.c
+e-util/e-html-editor-actions.c
+e-util/e-html-editor-cell-dialog.c
+e-util/e-html-editor-find-dialog.c
+e-util/e-html-editor-hrule-dialog.c
+e-util/e-html-editor-image-dialog.c
+e-util/e-html-editor-link-dialog.c
+e-util/e-html-editor-page-dialog.c
+e-util/e-html-editor-paragraph-dialog.c
+e-util/e-html-editor-replace-dialog.c
+e-util/e-html-editor-spell-check-dialog.c
+e-util/e-html-editor-table-dialog.c
+e-util/e-html-editor-text-dialog.c
+e-util/e-html-editor-view.c
+e-util/e-html-editor.c
e-util/e-image-chooser.c
e-util/e-import-assistant.c
e-util/e-interval-chooser.c
@@ -255,6 +272,7 @@ e-util/e-send-options.c
e-util/e-source-config-dialog.c
e-util/e-source-config.c
e-util/e-source-selector-dialog.c
+e-util/e-spell-dictionary.c
e-util/e-spell-entry.c
e-util/e-system.error.xml
e-util/e-table-click-to-add.c
@@ -277,7 +295,6 @@ e-util/e-timezone-dialog.c
e-util/e-tree-selection-model.c
e-util/e-tree.c
e-util/e-url-entry.c
-e-util/e-web-view-gtkhtml.c
e-util/e-web-view.c
e-util/e-widget-undo.c
e-util/ea-calendar-item.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 5abf2e7de8..37818a4f74 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -2,3 +2,4 @@ data/evolution-alarm-notify.desktop.in
data/evolution.desktop.in
designs/OOA/ooa.ui
designs/read_receipts/read.ui
+e-util/test-html-editor.c
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 686b8c37aa..29fdb278ab 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -61,9 +61,9 @@ libevolution_shell_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-shell\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CLUTTER_GTK_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_shell_la_SOURCES = \
$(evolution_shell_include_HEADERS) \
@@ -122,9 +122,9 @@ evolution_CPPFLAGS = \
-DLIBDIR=\""$(datadir)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CLUTTER_GTK_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
evolution_SOURCES = \
main.c \
diff --git a/smime/gui/Makefile.am b/smime/gui/Makefile.am
index 5d3f51af18..c71ac454da 100644
--- a/smime/gui/Makefile.am
+++ b/smime/gui/Makefile.am
@@ -17,9 +17,9 @@ libevolution_smime_la_CPPFLAGS = \
-DPREFIX=\""$(prefix)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CERT_UI_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libevolution_smime_la_SOURCES = \
ca-trust-dialog.c \
@@ -40,8 +40,8 @@ libevolution_smime_la_LIBADD = \
$(top_builddir)/smime/lib/libessmime.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(CERT_UI_LIBS)
+ $(CERT_UI_LIBS) \
+ $(NULL)
libevolution_smime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/smime/lib/Makefile.am b/smime/lib/Makefile.am
index 811c77831e..6e7264ec40 100644
--- a/smime/lib/Makefile.am
+++ b/smime/lib/Makefile.am
@@ -14,9 +14,9 @@ libessmime_la_CPPFLAGS = \
-DPREFIX=\""$(prefix)"\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(GTKHTML_CFLAGS) \
$(CERT_UI_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
libessmime_la_SOURCES = \
e-cert.c \
@@ -32,8 +32,8 @@ libessmime_la_LIBADD = \
$(top_builddir)/e-util/libevolution-util.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(GTKHTML_LIBS) \
- $(CERT_UI_LIBS)
+ $(CERT_UI_LIBS) \
+ $(NULL)
libessmime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)