[geary] Allow HTML signatures



commit c87bbea3ab06121f5fbb23658cb2320efd55aad8
Author: Robert Schroll <rschroll gmail com>
Date:   Mon Dec 1 20:14:21 2014 -0500

    Allow HTML signatures
    
    We search for HTML-like tags in signatures, and don't do much as much
    escaping if we find one.  For .signature files detected to be HTML, we
    insert them without any change.  User-entered signatures get their
    whitespace protected, even when HTML is detected. The existing code for
    preserving whitespace doesn't work when there's already HTML code in the
    text (it converts "<a b>" to "<a&nbsp;b>"), so instead we preserve the
    whitespace with CSS.
    
    A preview of the signature is added to the the UI.  There's a TextView
    and a WebView in a Stack, and we swap between them with a StackSwitcher.
    Some of the packing details are changed so that these views are the
    thing that expands when the dialog size changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=738895

 .../accounts/account-dialog-add-edit-pane.vala     |    2 +-
 src/client/accounts/add-edit-page.vala             |   42 ++++++++++++++++----
 src/client/composer/composer-widget.vala           |   10 ++--
 src/engine/util/util-html.vala                     |   15 +++++++
 ui/login.glade                                     |   24 +++++-------
 5 files changed, 65 insertions(+), 28 deletions(-)
---
diff --git a/src/client/accounts/account-dialog-add-edit-pane.vala 
b/src/client/accounts/account-dialog-add-edit-pane.vala
index 80ad80e..4a90f45 100644
--- a/src/client/accounts/account-dialog-add-edit-pane.vala
+++ b/src/client/accounts/account-dialog-add-edit-pane.vala
@@ -38,7 +38,7 @@ public class AccountDialogAddEditPane : AccountDialogPane {
         add_edit_page.size_changed.connect(() => { size_changed(); } );
         
         pack_start(add_edit_page);
-        pack_start(button_box);
+        pack_start(button_box, false, false);
         
         // Default mode is Welcome.
         set_mode(AddEditPage.PageMode.WELCOME);
diff --git a/src/client/accounts/add-edit-page.vala b/src/client/accounts/add-edit-page.vala
index 567b529..a78d5df 100644
--- a/src/client/accounts/add-edit-page.vala
+++ b/src/client/accounts/add-edit-page.vala
@@ -173,7 +173,9 @@ public class AddEditPage : Gtk.Box {
     // Signature
     private Gtk.Box composer_container;
     private Gtk.CheckButton check_use_email_signature;
+    private Gtk.Stack signature_stack;
     private Gtk.TextView textview_email_signature;
+    private StylishWebView preview_webview;
     
     private Gtk.Alignment other_info;
     
@@ -257,7 +259,32 @@ public class AddEditPage : Gtk.Box {
         // composer options
         composer_container = (Gtk.Box) builder.get_object("composer container");
         check_use_email_signature = (Gtk.CheckButton) builder.get_object("check: use_email_signature");
-        textview_email_signature = (Gtk.TextView) builder.get_object("textview: email_signature");        
+        
+        Gtk.ScrolledWindow edit_window = new Gtk.ScrolledWindow(null, null);
+        edit_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+        edit_window.set_shadow_type(Gtk.ShadowType.IN);
+        textview_email_signature = new Gtk.TextView();
+        edit_window.add(textview_email_signature);
+        
+        Gtk.ScrolledWindow preview_window = new Gtk.ScrolledWindow(null, null);
+        preview_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+        preview_window.set_shadow_type(Gtk.ShadowType.IN);
+        preview_webview = new StylishWebView();
+        preview_window.add(preview_webview);
+        
+        signature_stack = new Gtk.Stack();
+        signature_stack.add_titled(edit_window, "edit_window", _("Edit"));
+        signature_stack.child_set_property(edit_window, "icon-name", "text-editor-symbolic");
+        signature_stack.add_titled(preview_window, "preview_window", _("Preview"));
+        signature_stack.child_set_property(preview_window, "icon-name", "text-x-generic-symbolic");
+        Gtk.StackSwitcher switcher = new Gtk.StackSwitcher();
+        switcher.set_stack(signature_stack);
+        
+        Gtk.Box signature_box = (Gtk.Box) builder.get_object("signature box");
+        signature_box.set_spacing(4);
+        signature_box.pack_start(signature_stack);
+        switcher.valign = Gtk.Align.START;
+        signature_box.pack_start(switcher, false, false);
         
         // IMAP info widgets.
         entry_imap_host = (Gtk.Entry) builder.get_object("entry: imap host");
@@ -316,7 +343,8 @@ public class AddEditPage : Gtk.Box {
         
         entry_nickname.insert_text.connect(on_nickname_insert_text);
 
-        check_use_email_signature.toggled.connect(() => on_use_signature_changed());
+        check_use_email_signature.bind_property("active", signature_box, "sensitive");
+        signature_stack.notify["visible-child-name"].connect(on_signature_stack_changed);
         
         // Reset the "first update" flag when the window is mapped.
         map.connect(() => { first_ui_update = true; });
@@ -394,6 +422,7 @@ public class AddEditPage : Gtk.Box {
         combo_smtp_encryption.active = Encryption.NONE;
         use_email_signature = initial_use_email_signature;
         email_signature = initial_email_signature;
+        signature_stack.set_visible_child_name("edit_window");
         
         // Set defaults for IMAP info
         imap_host = initial_default_imap_host ?? "";
@@ -550,12 +579,9 @@ public class AddEditPage : Gtk.Box {
         }
     }
     
-    private void on_use_signature_changed() {
-        if(check_use_email_signature.active == true) {
-            textview_email_signature.sensitive = true;
-        } else {
-            textview_email_signature.sensitive = false;
-        }
+    private void on_signature_stack_changed() {
+        if (signature_stack.visible_child_name == "preview_window")
+            preview_webview.load_html_string(Geary.HTML.smart_escape(email_signature, true), "");
     }
 
     private uint16 get_default_smtp_port() {
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 09a48cf..78a6a3d 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -993,6 +993,7 @@ public class ComposerWidget : Gtk.EventBox {
                     set_cursor();
                     return;
                 }
+                signature = Geary.HTML.smart_escape(signature, false);
             } catch (Error error) {
                 debug("Error reading signature file %s: %s", signature_file.get_path(), error.message);
                 set_cursor();
@@ -1004,16 +1005,15 @@ public class ComposerWidget : Gtk.EventBox {
                 set_cursor();
                 return;
             }
+            signature = Geary.HTML.smart_escape(signature, true);
         }
         
-        signature = Geary.HTML.escape_markup(signature);
-        
         if (body_html == null)
-            body_html = CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature);
+            body_html = CURSOR + "<br /><br />" + signature;
         else if (top_posting)
-            body_html = CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature) + body_html;
+            body_html = CURSOR + "<br /><br />" + signature + body_html;
         else
-            body_html = body_html + CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature);
+            body_html = body_html + CURSOR + "<br /><br />" + signature;
     }
     
     private void set_cursor() {
diff --git a/src/engine/util/util-html.vala b/src/engine/util/util-html.vala
index ce34ec5..561c9d9 100644
--- a/src/engine/util/util-html.vala
+++ b/src/engine/util/util-html.vala
@@ -88,6 +88,21 @@ public string preserve_whitespace(string? text) {
     return output;
 }
 
+public string smart_escape(string? text, bool preserve_whitespace_in_html) {
+    if (text == null)
+        return text;
+    
+    string res = text;
+    if (!Regex.match_simple("<([A-Z]*)[^>]*>.*</(\\1)>|<[^>]*/>", res,
+        RegexCompileFlags.CASELESS)) {
+        res = escape_markup(res);
+        preserve_whitespace_in_html = true;
+    }
+    if (preserve_whitespace_in_html)
+        res = @"<div style='white-space: pre;'>$res</div>";
+    return res;
+}
+
 // Removes any text between < and >.  Additionally, if input terminates in the middle of a tag, 
 // the tag will be removed.
 // If the HTML is invalid, the original string will be returned.
diff --git a/ui/login.glade b/ui/login.glade
index 390e789..1e85ad0 100644
--- a/ui/login.glade
+++ b/ui/login.glade
@@ -7,7 +7,6 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkTextBuffer" id="buffer: email_signature"/>
   <object class="GtkBox" id="container">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -960,7 +959,7 @@
         </child>
         <child>
           <object class="GtkCheckButton" id="check: use_email_signature">
-            <property name="label" translatable="yes">Si_gn emails:</property>
+            <property name="label" translatable="yes">Si_gn emails (HTML allowed):</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">False</property>
@@ -976,23 +975,20 @@
           </packing>
         </child>
         <child>
-          <object class="GtkScrolledWindow" id="scrolledwindow2">
+          <object class="GtkBox" id="signature box">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
+            <property name="sensitive">False</property>
+            <property name="can_focus">False</property>
             <property name="margin_left">12</property>
-            <property name="shadow_type">in</property>
             <child>
-              <object class="GtkTextView" id="textview: email_signature">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-                <property name="can_focus">True</property>
-                <property name="wrap_mode">word</property>
-                <property name="buffer">buffer: email_signature</property>
-              </object>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
             </child>
           </object>
           <packing>
-            <property name="expand">False</property>
+            <property name="expand">True</property>
             <property name="fill">True</property>
             <property name="position">3</property>
           </packing>
@@ -1002,7 +998,7 @@
         </child>
       </object>
       <packing>
-        <property name="expand">False</property>
+        <property name="expand">True</property>
         <property name="fill">True</property>
         <property name="position">4</property>
       </packing>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]