[geary/wip/765516-gtk-widget-conversation-viewer: 85/187] General clean up of conversation viewer code.



commit 2444ca75579b1a205f6d5c525c25d863060dc2fd
Author: Michael James Gratton <mike vee net>
Date:   Tue Jun 21 13:52:20 2016 +1000

    General clean up of conversation viewer code.
    
    Ensure all public members have semi-decent documentaion comments. Make
    public members private if they don't need to be public. Re-org non-public
    members to be below public memebers in the source.

 .../conversation-viewer/conversation-email.vala    |   67 ++++--
 .../conversation-viewer/conversation-message.vala  |   61 ++++--
 .../conversation-viewer/conversation-viewer.vala   |  225 ++++++++++++--------
 3 files changed, 229 insertions(+), 124 deletions(-)
---
diff --git a/src/client/conversation-viewer/conversation-email.vala 
b/src/client/conversation-viewer/conversation-email.vala
index 1ac1a2f..7570b37 100644
--- a/src/client/conversation-viewer/conversation-email.vala
+++ b/src/client/conversation-viewer/conversation-email.vala
@@ -9,7 +9,7 @@
 /**
  * A widget for displaying an email in a conversation.
  *
- * This widget corresponds to {@link Geary.Email}, displaying the
+ * This view corresponds to {@link Geary.Email}, displaying the
  * email's primary message (a {@link Geary.RFC822.Message}), any
  * sub-messages (also instances of {@link Geary.RFC822.Message}) and
  * attachments. The RFC822 messages are themselves displayed by {@link
@@ -54,16 +54,16 @@ public class ConversationEmail : Gtk.Box {
     private const string ACTION_VIEW_SOURCE = "view_source";
 
 
-    // The email message being displayed
+    /** The specific email that is displayed by this view. */
     public Geary.Email email { get; private set; }
 
-    // Is the message body shown or not?
-    public bool is_message_body_visible = false;
+    /** Determines if the email is showing a preview or the full message. */
+    public bool is_collapsed = true;
 
-    // View displaying the email's primary message
+    /** The view displaying the email's primary message headers and body. */
     public ConversationMessage primary_message { get; private set; }
 
-    // Views for messages that are attachments
+    /** Views for attached messages. */
     public Gee.List<ConversationMessage> attached_messages {
         owned get { return this._attached_messages.read_only_view; }
     }
@@ -125,44 +125,51 @@ public class ConversationEmail : Gtk.Box {
 
     private Gtk.Menu attachments_menu;
 
-    // Fired when the user clicks "reply" in the message menu.
+    /** Fired when the user clicks "reply" in the message menu. */
     public signal void reply_to_message(Geary.Email message);
 
-    // Fired when the user clicks "reply all" in the message menu.
+    /** Fired when the user clicks "reply all" in the message menu. */
     public signal void reply_all_message(Geary.Email message);
 
-    // Fired when the user clicks "forward" in the message menu.
+    /** Fired when the user clicks "forward" in the message menu. */
     public signal void forward_message(Geary.Email message);
 
-    // Fired when the user updates the message's flags.
+    /** Fired when the user updates the email's flags. */
     public signal void mark_email(
         Geary.Email email, Geary.NamedFlag? to_add, Geary.NamedFlag? to_remove
     );
 
-    // Fired when the user updates all message's flags from this down.
+    /** Fired when the user updates flags for this email and all emails down. */
     public signal void mark_email_from(
         Geary.Email email, Geary.NamedFlag? to_add, Geary.NamedFlag? to_remove
     );
 
-    // Fired on message image save action is activated
+    /** Fired when the user saves an inline displayed image. */
     public signal void save_image(string? filename, Geary.Memory.Buffer buffer);
 
-    // Fired on link activation in the web_view
+    /** Fired when the user clicks a link in the email. */
     public signal void link_activated(string link);
 
-    // Fired on attachment activation
+    /** Fired when the user activates an attachment. */
     public signal void attachments_activated(Gee.Collection<AttachmentInfo> attachments);
 
-    // Fired when the save attachments action is activated
+    /** Fired when the user saves an attachment. */
     public signal void save_attachments(Gee.Collection<AttachmentInfo> attachments);
 
-    // Fired the edit draft button is clicked.
+    /** Fired the edit draft button is clicked. */
     public signal void edit_draft(Geary.Email email);
 
-    // Fired when the view source action is activated
+    /** Fired when the view source action is activated. */
     public signal void view_source(Geary.Email email);
 
 
+    /**
+     * Constructs a new view to display an email.
+     *
+     * This method sets up most of the user interface for displaying
+     * the complete email, but does not attempt any possibly
+     * long-running loading processes.
+     */
     public ConversationEmail(Geary.Email email,
                              Geary.ContactStore contact_store,
                              bool is_draft) {
@@ -278,6 +285,13 @@ public class ConversationEmail : Gtk.Box {
         update_email_state(false);
     }
 
+    /**
+     * Starts loading the complete email.
+     *
+     * This method will load the avatar and message body for the
+     * primary message and any attached messages, as well as
+     * attachment names, types and icons.
+     */
     public async void start_loading(Cancellable load_cancelled) {
         yield primary_message.load_avatar(
             GearyApplication.instance.controller.avatar_session,
@@ -294,8 +308,11 @@ public class ConversationEmail : Gtk.Box {
         yield load_attachments(load_cancelled);
     }
 
+    /**
+     * Shows the complete message: headers, body and attachments.
+     */
     public void expand_email(bool include_transitions=true) {
-        is_message_body_visible = true;
+        is_collapsed = false;
         get_style_context().add_class("geary_show_body");
         attachments_button.set_sensitive(true);
         star_button.set_sensitive(true);
@@ -304,8 +321,11 @@ public class ConversationEmail : Gtk.Box {
         primary_message.show_message_body(include_transitions);
     }
 
+    /**
+     * Hides the complete message, just showing the header preview.
+     */
     public void collapse_email() {
-        is_message_body_visible = false;
+        is_collapsed = true;
         get_style_context().remove_class("geary_show_body");
         attachments_button.set_sensitive(false);
         star_button.set_sensitive(false);
@@ -314,15 +334,24 @@ public class ConversationEmail : Gtk.Box {
         primary_message.hide_message_body();
     }
 
+    /**
+     * Updates the current email's flags and dependent UI state.
+     */
     public void update_flags(Geary.Email email) {
         this.email.set_flags(email.email_flags);
         update_email_state();
     }
 
+    /**
+     * Determines if the email is flagged as read on the client side only.
+     */
     public bool is_manual_read() {
         return get_style_context().has_class("geary_manual_read");
     }
 
+    /**
+     * Displays the message as read, even if not reflected in its flags.
+     */
     public void mark_manual_read() {
         get_style_context().add_class("geary_manual_read");
     }
diff --git a/src/client/conversation-viewer/conversation-message.vala 
b/src/client/conversation-viewer/conversation-message.vala
index 8633066..4f923dd 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -9,22 +9,22 @@
 /**
  * A widget for displaying an {@link Geary.RFC822.Message}.
  *
- * This widget corresponds to {@link Geary.RFC822.Message}, displaying
+ * This view corresponds to {@link Geary.RFC822.Message}, displaying
  * both the message's headers and body. Any attachments and sub
  * messages are handled by {@link ConversationEmail}, which typically
  * embeds at least one instance of this class.
  */
 [GtkTemplate (ui = "/org/gnome/Geary/conversation-message.ui")]
 public class ConversationMessage : Gtk.Box {
-    
 
-    // Internal class to associate inline image buffers (replaced by rotated scaled versions of
-    // them) so they can be saved intact if the user requires it
+    // Internal class to associate inline image buffers (replaced by
+    // rotated scaled versions of them) so they can be saved intact if
+    // the user requires it
     private class ReplacedImage : Geary.BaseObject {
         public string id;
         public string filename;
         public Geary.Memory.Buffer buffer;
-        
+
         public ReplacedImage(int replaced_number, string filename, Geary.Memory.Buffer buffer) {
             id = "%X".printf(replaced_number);
             this.filename = filename;
@@ -56,23 +56,25 @@ public class ConversationMessage : Gtk.Box {
     private const string ACTION_SELECT_ALL = "select_all";
 
 
-    // The message being displayed
+    /** The specific RFC822 message displayed by this view. */
     public Geary.RFC822.Message message { get; private set; }
 
-    // The HTML viewer to view the emails.
-    public ConversationWebView web_view { get; private set; }
-
-    // The allocation for the web view
+    /** Current allocated size of the HTML body view. */
     public Gdk.Rectangle web_view_allocation { get; private set; }
 
-    // Has the message body been been fully loaded?
+    /** Specifies if the message body been been fully loaded. */
     public bool is_loading_complete = false;
 
+    /** Box containing the preview and full header widgets.  */
     [GtkChild]
-    public Gtk.Box summary_box; // not yet supported: { get; private set; }
+    internal Gtk.Box summary_box;
 
+    /** Box that InfoBar widgets should be added to. */
     [GtkChild]
-    public Gtk.Box infobar_box; // not yet supported: { get; private set; }
+    internal Gtk.Box infobar_box;
+
+    /** HTML view that displays the message body. */
+    internal ConversationWebView web_view { get; private set; }
 
     [GtkChild]
     private Gtk.Revealer preview_revealer;
@@ -147,19 +149,26 @@ public class ConversationMessage : Gtk.Box {
     // Message-specific actions
     private SimpleActionGroup message_actions = new SimpleActionGroup();
 
-    // Fired when an attachment is displayed inline
+    /** Fired when an attachment is added for inline display. */
     public signal void attachment_displayed_inline(string id);
 
-    // Fired when remote image load requested for sender
+    /** Fired when the user requests remote images be loaded. */
     public signal void flag_remote_images();
 
-    // Fired when remote image load requested for sender
+    /** Fired when the user requests remote images be always loaded. */
     public signal void remember_remote_images();
 
-    // Fired on message image save action is activated
+    /** Fired when the user saves an inline displayed image. */
     public signal void save_image(string? filename, Geary.Memory.Buffer buffer);
 
 
+    /**
+     * Constructs a new view to display an RFC 823 message headers and body.
+     *
+     * This method sets up most of the user interface for displaying
+     * the message, but does not attempt any possibly long-running
+     * loading processes.
+     */
     public ConversationMessage(Geary.RFC822.Message message,
                                Geary.ContactStore contact_store,
                                bool always_load_remote_images) {
@@ -244,6 +253,9 @@ public class ConversationMessage : Gtk.Box {
         body_box.pack_start(web_view, true, true, 0);
     }
 
+    /**
+     * Shows the complete message and hides the preview headers.
+     */
     public void show_message_body(bool include_transitions=true) {
         Gtk.RevealerTransitionType revealer = preview_revealer.get_transition_type();
         if (!include_transitions) {
@@ -267,12 +279,18 @@ public class ConversationMessage : Gtk.Box {
         body_revealer.set_transition_type(revealer);
     }
 
+    /**
+     * Hides the complete message and shows the preview headers.
+     */
     public void hide_message_body() {
         preview_revealer.set_reveal_child(true);
         header_revealer.set_reveal_child(false);
         body_revealer.set_reveal_child(false);
     }
 
+    /**
+     * Starts loading the avatar for the sender of the message.
+     */
     public async void load_avatar(Soup.Session session, Cancellable load_cancellable) {
         // Queued messages are cancelled in ConversationViewer.clear()
         // rather than here using a callback on load_cancellable since
@@ -296,6 +314,9 @@ public class ConversationMessage : Gtk.Box {
         }
     }
 
+    /**
+     * Starts loading the message body in the HTML view.
+     */
     public async void load_message_body(Cancellable load_cancelled) {
         bool remote_images = false;
         bool load_images = false;
@@ -359,6 +380,9 @@ public class ConversationMessage : Gtk.Box {
         web_view.load_string(body_text, "text/html", "UTF8", "");
     }
 
+    /**
+     * Highlights user search terms in the message view.
+     */
     public void highlight_search_terms(Gee.Set<string> search_matches) {
         // XXX Need to highlight subject, sender and recipient matches too
 
@@ -381,6 +405,9 @@ public class ConversationMessage : Gtk.Box {
         web_view.set_highlight_text_matches(true);
     }
 
+    /**
+     * Disables highlighting of any search terms in the message view.
+     */
     public void unmark_search_terms() {
         web_view.set_highlight_text_matches(false);
         web_view.unmark_text_matches();
diff --git a/src/client/conversation-viewer/conversation-viewer.vala 
b/src/client/conversation-viewer/conversation-viewer.vala
index a14ac5c..518b6cd 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -7,14 +7,25 @@
  */
 
 /**
- * A Stack for managing the conversation pane and a {@link Geary.App.Conversation}.
+ * A widget for displaying conversations as a list of emails.
  *
- * Unlike ConversationListStore (which sorts by date received), ConversationViewer sorts by the
- * {@link Geary.Email.date} field (the Date: header), as that's the date displayed to the user.
+ * The view displays the current selected {@link
+ * Geary.App.Conversation} from the conversation list. To do so, it
+ * listens to signals from both the list and the current conversation
+ * monitor, updating the email list as needed.
+ *
+ * Unlike ConversationListStore (which sorts by date received),
+ * ConversationViewer sorts by the {@link Geary.Email.date} field (the
+ * Date: header), as that's the date displayed to the user.
+ *
+ * In addition to the email list, docked composers, a progress spinner
+ * and user messages are also displayed, depending on its current
+ * state.
  */
-
 [GtkTemplate (ui = "/org/gnome/Geary/conversation-viewer.ui")]
 public class ConversationViewer : Gtk.Stack {
+
+    /** Fields that must be available for display as a conversation. */
     public const Geary.Email.Field REQUIRED_FIELDS =
         Geary.Email.Field.HEADER
         | Geary.Email.Field.BODY
@@ -24,7 +35,7 @@ public class ConversationViewer : Gtk.Stack {
         | Geary.Email.Field.DATE
         | Geary.Email.Field.FLAGS
         | Geary.Email.Field.PREVIEW;
-    
+
     private const int SELECT_CONVERSATION_TIMEOUT_MSEC = 100;
 
     private enum ViewState {
@@ -32,7 +43,7 @@ public class ConversationViewer : Gtk.Stack {
         CONVERSATION,
         COMPOSE;
     }
-    
+
     private enum SearchState {
         // Search/find states.
         NONE,         // Not in search
@@ -52,22 +63,22 @@ public class ConversationViewer : Gtk.Stack {
         COUNT;
     }
 
-    // Fired when an email is added to the view
+    /** Fired when an email view is added to the conversation list. */
     public signal void email_row_added(ConversationEmail email);
 
-    // Fired when an email is removed from the view
+    /** Fired when an email view is removed from the conversation list. */
     public signal void email_row_removed(ConversationEmail email);
 
-    // Fired when the user marks messages.
+    /** Fired when the user updates the flags for a set of emails. */
     public signal void mark_emails(Gee.Collection<Geary.EmailIdentifier> emails,
         Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove);
 
-    // Fired when the viewer has been cleared.
+    /** Fired when the email list has been cleared. */
     public signal void cleared();
-    
-    // Current conversation, or null if none.
+
+    /** Current conversation being displayed, or null if none. */
     public Geary.App.Conversation? current_conversation = null;
-    
+
     // Stack pages
     [GtkChild]
     private Gtk.Image splash_page;
@@ -110,7 +121,10 @@ public class ConversationViewer : Gtk.Stack {
     private Geary.State.Machine fsm;
     private uint select_conversation_timeout_id = 0;
     private bool loading_conversations = false;
-    
+
+    /**
+     * Constructs a new conversation view instance.
+     */
     public ConversationViewer() {
         // Setup the conversation list box
         conversation_listbox.set_sort_func((row1, row2) => {
@@ -131,10 +145,10 @@ public class ConversationViewer : Gtk.Stack {
                 // embedded composer and should not be activated.
                 ConversationEmail? msg = row.get_child() as ConversationEmail;
                 if (!row.get_style_context().has_class("geary_last") && msg != null) {
-                    if (msg.is_message_body_visible) {
-                        collapse_email(row);
-                    } else {
+                    if (msg.is_collapsed) {
                         expand_email(row);
+                    } else {
+                        collapse_email(row);
                     }
                 }
             });
@@ -194,11 +208,17 @@ public class ConversationViewer : Gtk.Stack {
 
         do_conversation();
     }
-    
+
+    /**
+     * Returns the last email in the list by sort order, if any.
+     */
     public Geary.Email? get_last_email() {
         return emails.is_empty ? null : emails.last();
     }
-    
+
+    /**
+     * Returns the email with text currently selected, if any.
+     */
     public Geary.Email? get_selected_email(out string? quote) {
         // XXX check to see if there is a email with selected text,
         // if so return that
@@ -206,56 +226,9 @@ public class ConversationViewer : Gtk.Stack {
         return emails.is_empty ? null : emails.last();
     }
 
-    public void check_mark_read() {
-        Gee.ArrayList<Geary.EmailIdentifier> email_ids =
-            new Gee.ArrayList<Geary.EmailIdentifier>();
-
-        Gtk.Adjustment adj = conversation_page.vadjustment;
-        int top_bound = (int) adj.value;
-        int bottom_bound = top_bound + (int) adj.page_size;
-
-        const int TEXT_PADDING = 50;
-        foreach (Geary.Email email in emails) {
-            ConversationEmail conversation_email = conversation_email_for_id(email.id);
-            ConversationMessage conversation_message =
-                conversation_email.primary_message;
-            // Don't bother with not-yet-loaded emails since the
-            // size of the body will be off, affecting the visibility
-            // of emails further down the conversation.
-            if (email.email_flags.is_unread() &&
-                conversation_message.is_loading_complete &&
-                !conversation_email.is_manual_read()) {
-                 int body_top = 0;
-                 int body_left = 0;
-                 conversation_message.web_view.translate_coordinates(
-                     conversation_listbox,
-                     0, 0,
-                     out body_left, out body_top
-                 );
-                 int body_bottom = body_top +
-                     conversation_message.web_view_allocation.height;
-
-                 // Only mark the email as read if it's actually visible
-                 if (body_bottom > top_bound &&
-                     body_top + TEXT_PADDING < bottom_bound) {
-                     email_ids.add(email.id);
-
-                     // Since it can take some time for the new flags
-                     // to round-trip back to ConversationViewer's
-                     // signal handlers, mark as manually read here
-                     conversation_email.mark_manual_read();
-                 }
-             }
-        }
-
-        if (email_ids.size > 0) {
-            Geary.EmailFlags flags = new Geary.EmailFlags();
-            flags.add(Geary.EmailFlags.UNREAD);
-            mark_emails(email_ids, null, flags);
-        }
-    }
-
-    // Use this when an email has been marked read through manual (user) intervention
+    /**
+     * Displays an email as being read, regardless of its actual flags.
+     */
     public void mark_manual_read(Geary.EmailIdentifier id) {
         ConversationEmail? row = conversation_email_for_id(id);
         if (row != null) {
@@ -263,13 +236,19 @@ public class ConversationViewer : Gtk.Stack {
         }
     }
 
+    /**
+     * Hides a specific email in the conversation.
+     */
     public void blacklist_by_id(Geary.EmailIdentifier? id) {
         if (id == null) {
             return;
         }
         email_to_row.get(id).hide();
     }
-    
+
+    /**
+     * Re-displays a previously blacklisted email.
+     */
     public void unblacklist_by_id(Geary.EmailIdentifier? id) {
         if (id == null) {
             return;
@@ -277,11 +256,17 @@ public class ConversationViewer : Gtk.Stack {
         email_to_row.get(id).show();
     }
 
+    /**
+     * Puts the view into conversation mode, showing the email list.
+     */
     public void do_conversation() {
         state = ViewState.CONVERSATION;
         set_visible_child(loading_page);
     }
-    
+
+    /**
+     * Puts the view into composer mode, showing a full-height composer.
+     */
     public void do_compose(ComposerWidget composer) {
         state = ViewState.COMPOSE;
         ComposerBox box = new ComposerBox(composer);
@@ -302,10 +287,13 @@ public class ConversationViewer : Gtk.Stack {
         composer_page.pack_start(box);
         set_visible_child(composer_page);
     }
-    
+
+    /**
+     * Puts the view into conversation mode, but with an embedded composer.
+     */
     public void do_embedded_composer(ComposerWidget composer, Geary.Email referred) {
         state = ViewState.CONVERSATION;
-        
+
         ComposerEmbed embed = new ComposerEmbed(
             referred, composer, conversation_page
         );
@@ -326,11 +314,84 @@ public class ConversationViewer : Gtk.Stack {
 
     }
 
-    public new void set_visible_child(Gtk.Widget widget) {
+    /**
+     * Shows the in-conversation search UI.
+     */
+    public void show_find_bar() {
+        fsm.issue(SearchEvent.OPEN_FIND_BAR);
+        conversation_find_bar.focus_entry();
+    }
+
+    /**
+     * Displays the next/previous match for an in-conversation search.
+     */
+    public void find(bool forward) {
+        if (!conversation_find_bar.visible)
+            show_find_bar();
+
+        conversation_find_bar.find(forward);
+    }
+
+    /**
+     * Sets the currently visible page of the stack.
+     */
+    private new void set_visible_child(Gtk.Widget widget) {
         debug("Showing child: %s\n", widget.get_name());
         base.set_visible_child(widget);
     }
 
+    /**
+     * Finds any currently visible messages, marks them as being read.
+     */
+    private void check_mark_read() {
+        Gee.ArrayList<Geary.EmailIdentifier> email_ids =
+            new Gee.ArrayList<Geary.EmailIdentifier>();
+
+        Gtk.Adjustment adj = conversation_page.vadjustment;
+        int top_bound = (int) adj.value;
+        int bottom_bound = top_bound + (int) adj.page_size;
+
+        const int TEXT_PADDING = 50;
+        foreach (Geary.Email email in emails) {
+            ConversationEmail conversation_email = conversation_email_for_id(email.id);
+            ConversationMessage conversation_message =
+                conversation_email.primary_message;
+            // Don't bother with not-yet-loaded emails since the
+            // size of the body will be off, affecting the visibility
+            // of emails further down the conversation.
+            if (email.email_flags.is_unread() &&
+                conversation_message.is_loading_complete &&
+                !conversation_email.is_manual_read()) {
+                 int body_top = 0;
+                 int body_left = 0;
+                 conversation_message.web_view.translate_coordinates(
+                     conversation_listbox,
+                     0, 0,
+                     out body_left, out body_top
+                 );
+                 int body_bottom = body_top +
+                     conversation_message.web_view_allocation.height;
+
+                 // Only mark the email as read if it's actually visible
+                 if (body_bottom > top_bound &&
+                     body_top + TEXT_PADDING < bottom_bound) {
+                     email_ids.add(email.id);
+
+                     // Since it can take some time for the new flags
+                     // to round-trip back to ConversationViewer's
+                     // signal handlers, mark as manually read here
+                     conversation_email.mark_manual_read();
+                 }
+             }
+        }
+
+        if (email_ids.size > 0) {
+            Geary.EmailFlags flags = new Geary.EmailFlags();
+            flags.add(Geary.EmailFlags.UNREAD);
+            mark_emails(email_ids, null, flags);
+        }
+    }
+
     // Removes all displayed e-mails from the view.
     private void clear() {
         // Cancel any pending avatar loads here, rather than in
@@ -684,23 +745,11 @@ public class ConversationViewer : Gtk.Stack {
     private void compress_emails() {
         conversation_listbox.get_style_context().add_class("geary_compressed");
     }
-    
+
     //private void decompress_emails() {
     //  conversation_listbox.get_style_context().remove_class("geary_compressed");
     //}
-    
-    public void show_find_bar() {
-        fsm.issue(SearchEvent.OPEN_FIND_BAR);
-        conversation_find_bar.focus_entry();
-    }
-    
-    public void find(bool forward) {
-        if (!conversation_find_bar.visible)
-            show_find_bar();
-        
-        conversation_find_bar.find(forward);
-    }
-    
+
     private void on_update_flags(Geary.Email email) {
         // Nothing to do if we aren't displaying this email.
         if (!email_to_row.has_key(email.id)) {


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