Optionally send email in plain text: Closes #3198
The composer can now be switched between rich and plain text mode using the context menu.
This commit is contained in:
parent
5328b3448f
commit
3e4b94de0e
4 changed files with 117 additions and 23 deletions
|
|
@ -64,6 +64,11 @@
|
||||||
<summary>ask when opening an attachment</summary>
|
<summary>ask when opening an attachment</summary>
|
||||||
<description>True to ask when opening an attachment.</description>
|
<description>True to ask when opening an attachment.</description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="compose-as-html" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>whether to compose emails in HTML</summary>
|
||||||
|
<description>True to compose emails in HTML; false for plain text.</description>
|
||||||
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ public class ComposerWindow : Gtk.Window {
|
||||||
private const string ACTION_FONT_SIZE = "fontsize";
|
private const string ACTION_FONT_SIZE = "fontsize";
|
||||||
private const string ACTION_COLOR = "color";
|
private const string ACTION_COLOR = "color";
|
||||||
private const string ACTION_INSERT_LINK = "insertlink";
|
private const string ACTION_INSERT_LINK = "insertlink";
|
||||||
|
private const string ACTION_COMPOSE_AS_HTML = "compose as html";
|
||||||
|
|
||||||
private const string URI_LIST_MIME_TYPE = "text/uri-list";
|
private const string URI_LIST_MIME_TYPE = "text/uri-list";
|
||||||
private const string FILE_URI_PREFIX = "file://";
|
private const string FILE_URI_PREFIX = "file://";
|
||||||
|
|
@ -49,6 +50,9 @@ public class ComposerWindow : Gtk.Window {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
font-size: medium !important;
|
font-size: medium !important;
|
||||||
}
|
}
|
||||||
|
body.plain {
|
||||||
|
font-family: monospace !important;
|
||||||
|
}
|
||||||
blockquote {
|
blockquote {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
|
@ -60,6 +64,10 @@ public class ComposerWindow : Gtk.Window {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-left: 3px #aaa solid;
|
border-left: 3px #aaa solid;
|
||||||
}
|
}
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head><body id="message-body"></body></html>""";
|
</head><body id="message-body"></body></html>""";
|
||||||
|
|
||||||
|
|
@ -101,6 +109,11 @@ public class ComposerWindow : Gtk.Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool compose_as_html {
|
||||||
|
get { return ((Gtk.ToggleAction) actions.get_action(ACTION_COMPOSE_AS_HTML)).active; }
|
||||||
|
set { ((Gtk.ToggleAction) actions.get_action(ACTION_COMPOSE_AS_HTML)).active = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public ComposeType compose_type { get; private set; default = ComposeType.NEW_MESSAGE; }
|
public ComposeType compose_type { get; private set; default = ComposeType.NEW_MESSAGE; }
|
||||||
|
|
||||||
private string? body_html = null;
|
private string? body_html = null;
|
||||||
|
|
@ -126,6 +139,7 @@ public class ComposerWindow : Gtk.Window {
|
||||||
private Gtk.Alignment visible_on_attachment_drag_over;
|
private Gtk.Alignment visible_on_attachment_drag_over;
|
||||||
private Gtk.Widget hidden_on_attachment_drag_over_child;
|
private Gtk.Widget hidden_on_attachment_drag_over_child;
|
||||||
private Gtk.Widget visible_on_attachment_drag_over_child;
|
private Gtk.Widget visible_on_attachment_drag_over_child;
|
||||||
|
private Gtk.Toolbar compose_toolbar;
|
||||||
|
|
||||||
private Gtk.RadioMenuItem font_small;
|
private Gtk.RadioMenuItem font_small;
|
||||||
private Gtk.RadioMenuItem font_medium;
|
private Gtk.RadioMenuItem font_medium;
|
||||||
|
|
@ -198,6 +212,8 @@ public class ComposerWindow : Gtk.Window {
|
||||||
subject_entry = builder.get_object("subject") as Gtk.Entry;
|
subject_entry = builder.get_object("subject") as Gtk.Entry;
|
||||||
Gtk.Alignment message_area = builder.get_object("message area") as Gtk.Alignment;
|
Gtk.Alignment message_area = builder.get_object("message area") as Gtk.Alignment;
|
||||||
actions = builder.get_object("compose actions") as Gtk.ActionGroup;
|
actions = builder.get_object("compose actions") as Gtk.ActionGroup;
|
||||||
|
// Can only hapen after actions exits
|
||||||
|
compose_as_html = GearyApplication.instance.config.compose_as_html;
|
||||||
|
|
||||||
// Listen to account signals to update from menu.
|
// Listen to account signals to update from menu.
|
||||||
Geary.Engine.instance.account_available.connect(update_from_field);
|
Geary.Engine.instance.account_available.connect(update_from_field);
|
||||||
|
|
@ -222,7 +238,7 @@ public class ComposerWindow : Gtk.Window {
|
||||||
cc_entry.changed.connect(validate_send_button);
|
cc_entry.changed.connect(validate_send_button);
|
||||||
bcc_entry.changed.connect(validate_send_button);
|
bcc_entry.changed.connect(validate_send_button);
|
||||||
|
|
||||||
Gtk.Toolbar compose_toolbar = (Gtk.Toolbar) builder.get_object("compose_toolbar");
|
compose_toolbar = (Gtk.Toolbar) builder.get_object("compose_toolbar");
|
||||||
|
|
||||||
actions.get_action(ACTION_UNDO).activate.connect(on_action);
|
actions.get_action(ACTION_UNDO).activate.connect(on_action);
|
||||||
actions.get_action(ACTION_REDO).activate.connect(on_action);
|
actions.get_action(ACTION_REDO).activate.connect(on_action);
|
||||||
|
|
@ -233,20 +249,21 @@ public class ComposerWindow : Gtk.Window {
|
||||||
actions.get_action(ACTION_PASTE).activate.connect(on_paste);
|
actions.get_action(ACTION_PASTE).activate.connect(on_paste);
|
||||||
actions.get_action(ACTION_PASTE_FORMAT).activate.connect(on_paste_with_formatting);
|
actions.get_action(ACTION_PASTE_FORMAT).activate.connect(on_paste_with_formatting);
|
||||||
|
|
||||||
actions.get_action(ACTION_BOLD).activate.connect(on_action);
|
actions.get_action(ACTION_BOLD).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_ITALIC).activate.connect(on_action);
|
actions.get_action(ACTION_ITALIC).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_UNDERLINE).activate.connect(on_action);
|
actions.get_action(ACTION_UNDERLINE).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_STRIKETHROUGH).activate.connect(on_action);
|
actions.get_action(ACTION_STRIKETHROUGH).activate.connect(on_formatting_action);
|
||||||
|
|
||||||
actions.get_action(ACTION_REMOVE_FORMAT).activate.connect(on_remove_format);
|
actions.get_action(ACTION_REMOVE_FORMAT).activate.connect(on_remove_format);
|
||||||
|
actions.get_action(ACTION_COMPOSE_AS_HTML).activate.connect(on_compose_as_html);
|
||||||
|
|
||||||
actions.get_action(ACTION_INDENT).activate.connect(on_action);
|
actions.get_action(ACTION_INDENT).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_OUTDENT).activate.connect(on_action);
|
actions.get_action(ACTION_OUTDENT).activate.connect(on_formatting_action);
|
||||||
|
|
||||||
actions.get_action(ACTION_JUSTIFY_LEFT).activate.connect(on_action);
|
actions.get_action(ACTION_JUSTIFY_LEFT).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_JUSTIFY_RIGHT).activate.connect(on_action);
|
actions.get_action(ACTION_JUSTIFY_RIGHT).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_JUSTIFY_CENTER).activate.connect(on_action);
|
actions.get_action(ACTION_JUSTIFY_CENTER).activate.connect(on_formatting_action);
|
||||||
actions.get_action(ACTION_JUSTIFY_FULL).activate.connect(on_action);
|
actions.get_action(ACTION_JUSTIFY_FULL).activate.connect(on_formatting_action);
|
||||||
|
|
||||||
actions.get_action(ACTION_FONT).activate.connect(on_select_font);
|
actions.get_action(ACTION_FONT).activate.connect(on_select_font);
|
||||||
actions.get_action(ACTION_FONT_SIZE).activate.connect(on_select_font_size);
|
actions.get_action(ACTION_FONT_SIZE).activate.connect(on_select_font_size);
|
||||||
|
|
@ -392,6 +409,9 @@ public class ComposerWindow : Gtk.Window {
|
||||||
editor.grab_focus();
|
editor.grab_focus();
|
||||||
body.focus();
|
body.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the editor is in correct mode re HTML
|
||||||
|
on_compose_as_html();
|
||||||
|
|
||||||
bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
|
bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
|
||||||
update_actions();
|
update_actions();
|
||||||
|
|
@ -504,7 +524,8 @@ public class ComposerWindow : Gtk.Window {
|
||||||
|
|
||||||
email.attachment_files.add_all(attachment_files);
|
email.attachment_files.add_all(attachment_files);
|
||||||
|
|
||||||
email.body_html = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(get_html()));
|
if (compose_as_html)
|
||||||
|
email.body_html = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(get_html()));
|
||||||
email.body_text = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(get_text()));
|
email.body_text = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(get_text()));
|
||||||
|
|
||||||
// User-Agent
|
// User-Agent
|
||||||
|
|
@ -671,6 +692,11 @@ public class ComposerWindow : Gtk.Window {
|
||||||
&& (!to_entry.empty || !cc_entry.empty || !bcc_entry.empty);
|
&& (!to_entry.empty || !cc_entry.empty || !bcc_entry.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void on_formatting_action(Gtk.Action action) {
|
||||||
|
if (compose_as_html)
|
||||||
|
on_action(action);
|
||||||
|
}
|
||||||
|
|
||||||
private void on_action(Gtk.Action action) {
|
private void on_action(Gtk.Action action) {
|
||||||
if (action_flag)
|
if (action_flag)
|
||||||
return;
|
return;
|
||||||
|
|
@ -786,6 +812,50 @@ public class ComposerWindow : Gtk.Window {
|
||||||
editor.get_dom_document().exec_command("forecolor", false, "#000000");
|
editor.get_dom_document().exec_command("forecolor", false, "#000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void on_compose_as_html() {
|
||||||
|
WebKit.DOM.DOMTokenList body_classes = editor.get_dom_document().body.get_class_list();
|
||||||
|
if (!compose_as_html) {
|
||||||
|
// Do the equivalent of on_remove_format, but for entire document while maintaining
|
||||||
|
// selection.
|
||||||
|
editor.settings.enable_scripts = true;
|
||||||
|
editor.execute_script("""
|
||||||
|
selection = document.getSelection();
|
||||||
|
anchorNode = selection.anchorNode;
|
||||||
|
anchorOffset = selection.anchorOffset;
|
||||||
|
focusNode = selection.focusNode;
|
||||||
|
focusOffset = selection.focusOffset;
|
||||||
|
|
||||||
|
selection.selectAllChildren(document);
|
||||||
|
|
||||||
|
document.execCommand("removeformat", false, "");
|
||||||
|
// TODO: Use this when a reset stylesheet is available:
|
||||||
|
// http://redmine.yorba.org/issues/6437
|
||||||
|
//document.execCommand("removeparaformat", false, "");
|
||||||
|
document.execCommand("unlink", false, "");
|
||||||
|
document.execCommand("backcolor", false, "#ffffff");
|
||||||
|
document.execCommand("forecolor", false, "#000000");
|
||||||
|
|
||||||
|
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
|
||||||
|
""");
|
||||||
|
editor.settings.enable_scripts = false;
|
||||||
|
|
||||||
|
compose_toolbar.hide();
|
||||||
|
try {
|
||||||
|
body_classes.add("plain");
|
||||||
|
} catch (Error error) {
|
||||||
|
debug("Error setting composer style: %s", error.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compose_toolbar.show();
|
||||||
|
try {
|
||||||
|
body_classes.remove("plain");
|
||||||
|
} catch (Error error) {
|
||||||
|
debug("Error setting composer style: %s", error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GearyApplication.instance.config.compose_as_html = compose_as_html;
|
||||||
|
}
|
||||||
|
|
||||||
private void on_select_font() {
|
private void on_select_font() {
|
||||||
if (!font_button.active)
|
if (!font_button.active)
|
||||||
return;
|
return;
|
||||||
|
|
@ -841,17 +911,18 @@ public class ComposerWindow : Gtk.Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_select_color() {
|
private void on_select_color() {
|
||||||
Gtk.ColorChooserDialog dialog = new Gtk.ColorChooserDialog("Select Color", this);
|
if (compose_as_html) {
|
||||||
if (dialog.run() == Gtk.ResponseType.OK) {
|
Gtk.ColorChooserDialog dialog = new Gtk.ColorChooserDialog(_("Select Color"), this);
|
||||||
string color = dialog.get_rgba().to_string();
|
if (dialog.run() == Gtk.ResponseType.OK)
|
||||||
editor.get_dom_document().exec_command("forecolor", false, color);
|
editor.get_dom_document().exec_command("forecolor", false, dialog.get_rgba().to_string());
|
||||||
|
|
||||||
|
dialog.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_insert_link() {
|
private void on_insert_link() {
|
||||||
link_dialog("http://");
|
if (compose_as_html)
|
||||||
|
link_dialog("http://");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void on_link_clicked(WebKit.DOM.Element element, WebKit.DOM.Event event,
|
private static void on_link_clicked(WebKit.DOM.Element element, WebKit.DOM.Event event,
|
||||||
|
|
@ -1014,9 +1085,11 @@ public class ComposerWindow : Gtk.Window {
|
||||||
context_menu.append(paste);
|
context_menu.append(paste);
|
||||||
|
|
||||||
// Paste with formatting
|
// Paste with formatting
|
||||||
Gtk.MenuItem paste_format = new Gtk.ImageMenuItem();
|
if (compose_as_html) {
|
||||||
paste_format.related_action = actions.get_action(ACTION_PASTE_FORMAT);
|
Gtk.MenuItem paste_format = new Gtk.ImageMenuItem();
|
||||||
context_menu.append(paste_format);
|
paste_format.related_action = actions.get_action(ACTION_PASTE_FORMAT);
|
||||||
|
context_menu.append(paste_format);
|
||||||
|
}
|
||||||
|
|
||||||
context_menu.append(new Gtk.SeparatorMenuItem());
|
context_menu.append(new Gtk.SeparatorMenuItem());
|
||||||
|
|
||||||
|
|
@ -1025,6 +1098,11 @@ public class ComposerWindow : Gtk.Window {
|
||||||
select_all_item.activate.connect(on_select_all);
|
select_all_item.activate.connect(on_select_all);
|
||||||
context_menu.append(select_all_item);
|
context_menu.append(select_all_item);
|
||||||
|
|
||||||
|
// HTML or plain text
|
||||||
|
Gtk.CheckMenuItem html_item = new Gtk.CheckMenuItem();
|
||||||
|
html_item.related_action = actions.get_action(ACTION_COMPOSE_AS_HTML);
|
||||||
|
context_menu.append(html_item);
|
||||||
|
|
||||||
context_menu.show_all();
|
context_menu.show_all();
|
||||||
context_menu.popup(null, null, null, event.button, event.time);
|
context_menu.popup(null, null, null, event.button, event.time);
|
||||||
|
|
||||||
|
|
@ -1043,7 +1121,7 @@ public class ComposerWindow : Gtk.Window {
|
||||||
actions.get_action(ACTION_COPY).sensitive = editor.can_copy_clipboard();
|
actions.get_action(ACTION_COPY).sensitive = editor.can_copy_clipboard();
|
||||||
actions.get_action(ACTION_COPY_LINK).sensitive = hover_url != null;
|
actions.get_action(ACTION_COPY_LINK).sensitive = hover_url != null;
|
||||||
actions.get_action(ACTION_PASTE).sensitive = editor.can_paste_clipboard();
|
actions.get_action(ACTION_PASTE).sensitive = editor.can_paste_clipboard();
|
||||||
actions.get_action(ACTION_PASTE_FORMAT).sensitive = editor.can_paste_clipboard();
|
actions.get_action(ACTION_PASTE_FORMAT).sensitive = editor.can_paste_clipboard() && compose_as_html;
|
||||||
|
|
||||||
// Style toggle buttons.
|
// Style toggle buttons.
|
||||||
WebKit.DOM.DOMWindow window = editor.get_dom_document().get_default_view();
|
WebKit.DOM.DOMWindow window = editor.get_dom_document().get_default_view();
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,12 @@ public class Configuration {
|
||||||
set { set_boolean(ASK_OPEN_ATTACHMENT, value); }
|
set { set_boolean(ASK_OPEN_ATTACHMENT, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string COMPOSE_AS_HTML = "compose-as-html";
|
||||||
|
public bool compose_as_html {
|
||||||
|
get { return settings.get_boolean(COMPOSE_AS_HTML); }
|
||||||
|
set { set_boolean(COMPOSE_AS_HTML, value); }
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a configuration object.
|
// Creates a configuration object.
|
||||||
public Configuration() {
|
public Configuration() {
|
||||||
// Start GSettings.
|
// Start GSettings.
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,11 @@
|
||||||
<property name="icon_name">font</property>
|
<property name="icon_name">font</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToggleAction" id="compose as html">
|
||||||
|
<property name="label" translatable="yes">Rich text</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkBox" id="composer">
|
<object class="GtkBox" id="composer">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue