[Xfce4-commits] <postler:master> Backend-side flag changes and filename timestamps

Christian Dywan noreply at xfce.org
Sun May 29 04:46:04 CEST 2011


Updating branch refs/heads/master
         to 3bcde8934d2aea7ad4f742188e18af6b94e17bd0 (commit)
       from 94cff9dd02321d3aadfa9311e8acd48193db5eb8 (commit)

commit 3bcde8934d2aea7ad4f742188e18af6b94e17bd0
Author: Christian Dywan <christian at twotoasts.de>
Date:   Sat May 28 19:11:02 2011 +0200

    Backend-side flag changes and filename timestamps
    
    The backend takes care of changing flags and updating
    filenames, and returning the result through DBus now.
    
    The new enumeration for flags makes it clearer what
    the result of a change will be.
    
    Showing a message or new window happens in an asynchronous
    callback after updating the flag.
    
    Timestamps are recorded whenever filenames are inserted
    so we can tell that files disappeared between runs.
    
    Folders are monitored for new and removed messages.

 postler/postler-bureau.vala   |    4 +-
 postler/postler-client.vala   |    9 +++--
 postler/postler-index.vala    |   70 ++++++++++++++++++++++++++++++----------
 postler/postler-message.vala  |   24 ++++++++++++--
 postler/postler-messages.vala |   45 ++++++++++++--------------
 postler/postler-service.vala  |   54 +++++++++++++++++++++++++++-----
 6 files changed, 147 insertions(+), 59 deletions(-)

diff --git a/postler/postler-bureau.vala b/postler/postler-bureau.vala
index 8d623b2..922ec21 100644
--- a/postler/postler-bureau.vala
+++ b/postler/postler-bureau.vala
@@ -196,11 +196,11 @@ public class Postler.Bureau : Gtk.Window {
     }
 
     void action_message_unread () {
-        messages.toggle_selected_flag ('S');
+        messages.toggle_selected_flag (MessageFlags.TOGGLE_READ);
     }
 
     void action_flag () {
-        messages.toggle_selected_flag ('F');
+        messages.toggle_selected_flag (MessageFlags.TOGGLE_FLAGGED);
     }
 
     void action_archive () {
diff --git a/postler/postler-client.vala b/postler/postler-client.vala
index 886994c..cb8d604 100644
--- a/postler/postler-client.vala
+++ b/postler/postler-client.vala
@@ -14,7 +14,8 @@ namespace Postler {
     [DBus (name = "org.elementary.Postler")]
     interface PostlerClient : Object {
         public async abstract int64 unread_messages (string uri) throws IOError;
-        public async abstract GLib.HashTable<string,Variant> get_message (string id) throws IOError;
+        public async abstract GLib.HashTable<string,Variant> get_message (string id,
+            char flag) throws IOError;
         public async abstract string[] get_messages (string uri) throws IOError;
         public signal void progress (string account, string text, double fraction);
         public abstract void receive (string account) throws IOError;
@@ -56,9 +57,11 @@ namespace Postler {
             return yield client.unread_messages (uri);
         }
 
-        public async void get_message (Message message, Gtk.TreeRowReference row) {
+        public async void get_message (Message message, char flag,
+            Gtk.TreeRowReference row) {
+
             try {
-                message.from_hash_table (yield client.get_message (message.id));
+                message.from_hash_table (yield client.get_message (message.id, flag));
             } catch (GLib.Error error) {
                 message.from_error (error.message.replace (
     "org.gtk.GDBus.UnmappedGError.Quark._g_2dfile_2derror_2dquark.Code24", "GLib.FileError"));
diff --git a/postler/postler-index.vala b/postler/postler-index.vala
index 95f15b1..5209a58 100644
--- a/postler/postler-index.vala
+++ b/postler/postler-index.vala
@@ -16,7 +16,13 @@ namespace Postler {
         static Sqlite.Statement? statement_get = null;
         static Sqlite.Statement? statement_list = null;
         static Sqlite.Statement? statement_count = null;
+        static Sqlite.Statement? statement_remove = null;
         static Sqlite.Statement? statement_unread = null;
+        static int64 startup_timestamp = new DateTime.now_utc ().to_unix ();
+
+        bool row_or_done (int result) {
+            return result == Sqlite.ROW || result == Sqlite.DONE;
+        }
 
         bool init (GLib.Cancellable? cancellable = null) throws GLib.Error {
             return true;
@@ -40,7 +46,7 @@ namespace Postler {
                 messages (id TEXT, uri TEXT, subject TEXT, sender TEXT, recipients TEXT,
                           unread BOOLEAN, flagged BOOLEAN,
                           forwarded BOOLEAN, replied BOOLEAN, priority BOOLEAN,
-                          date INTEGER, excerpt TEXT);
+                          date INTEGER, timestamp INTEGER, excerpt TEXT);
                 """
                 ) != Sqlite.OK)
                 throw new GLib.FileError.FAILED (_("Failed to open database: %s"), database.errmsg ());
@@ -49,10 +55,10 @@ namespace Postler {
         public void insert_received_message (Message message) throws GLib.Error {
             if (statement_insert == null) {
                 if (database.prepare_v2 ("""
-                    INSERT OR IGNORE INTO messages
+                    INSERT INTO messages
                     (uri, subject, sender, recipients,
-                     unread, flagged, forwarded, replied, priority, date, id)
-                    VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
+                     unread, flagged, forwarded, replied, priority, date, id, timestamp)
+                    VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
                     """,
                     -1, out statement_insert) != Sqlite.OK)
                     throw new GLib.FileError.FAILED (_("Failed to index message: %s"), database.errmsg ());
@@ -69,6 +75,7 @@ namespace Postler {
              && statement_insert.bind_int64 (9, message.priority ? 1 : 0) == Sqlite.OK
              && statement_insert.bind_int64 (10, message.get_timestamp ()) == Sqlite.OK
              && statement_insert.bind_text (11, message.id) == Sqlite.OK
+             && statement_insert.bind_int64 (12, startup_timestamp) == Sqlite.OK
              && statement_insert.step () == Sqlite.DONE;
             statement_insert.reset ();
             if (!success)
@@ -85,24 +92,51 @@ namespace Postler {
 #endif
         }
 
-        public int64 count_messages (string uri) throws GLib.Error {
+        public bool update_filename (string uri) throws GLib.Error {
             if (statement_count == null) {
                 if (database.prepare_v2 ("""
-                    SELECT COUNT (*) FROM messages WHERE uri LIKE ?1
+                    UPDATE messages SET timestamp = ?2 WHERE uri = ?1
                     """,
                     -1, out statement_count) != Sqlite.OK)
-                    throw new GLib.FileError.FAILED (_("Failed to open database: %s"), database.errmsg ());
+                    throw new GLib.FileError.FAILED (_("Failed to update filename: %s"), database.errmsg ());
             }
             bool success =
                 statement_count.bind_text (1, uri, -1) == Sqlite.OK
-             && statement_count.step () == Sqlite.ROW;
+             && statement_count.bind_int64 (2, startup_timestamp) == Sqlite.OK
+             && statement_count.step () == Sqlite.DONE;
             if (!success) {
                 statement_count.reset ();
-                throw new GLib.FileError.FAILED (_("Failed to open database: %s"), database.errmsg ());
+                throw new GLib.FileError.FAILED (_("Failed to update filename: %s"), database.errmsg ());
             }
-            int64 count = statement_count.column_int64 (0);
+            int64 updated_rows = database.changes ();
             statement_count.reset ();
-            return count;
+            return updated_rows > 0;
+        }
+
+        public void purge_filenames () throws GLib.Error {
+            if (database.exec (
+                "DELETE FROM messages WHERE timestamp < " + startup_timestamp.to_string ())
+                != Sqlite.OK)
+                throw new GLib.FileError.FAILED (_("Failed to purge old filenames: %s"), database.errmsg ());
+            startup_timestamp = new DateTime.now_utc ().to_unix ();
+        }
+
+        public void remove_filename (string uri) throws GLib.Error {
+            if (statement_remove == null) {
+                if (database.prepare_v2 ("""
+                    DELETE FROM messages WHERE uri = ?1
+                    """,
+                    -1, out statement_remove) != Sqlite.OK)
+                    throw new GLib.FileError.FAILED (_("Failed to remove filename: %s"), database.errmsg ());
+            }
+            bool success =
+                statement_remove.bind_text (1, uri, -1) == Sqlite.OK
+             && statement_remove.step () == Sqlite.DONE;
+            if (!success) {
+                statement_remove.reset ();
+                throw new GLib.FileError.FAILED (_("Failed to remove filename: %s"), database.errmsg ());
+            }
+            statement_remove.reset ();
         }
 
         public int64 unread_messages (string folder) throws GLib.Error {
@@ -129,15 +163,15 @@ namespace Postler {
             if (statement_get == null) {
                 if (database.prepare_v2 ("""
                     SELECT subject, sender, recipients,
-                    unread, flagged, forwarded, replied, priority, date, uri FROM messages WHERE id = ?1
+                    unread, flagged, forwarded, replied, priority, date, uri FROM messages
+                    WHERE id = ?1 GROUP BY id ORDER BY date ASC LIMIT 1
                     """,
                     -1, out statement_get) != Sqlite.OK)
                     throw new GLib.FileError.FAILED (_("Failed to list messages: %s"), database.errmsg ());
             }
-            int result = Sqlite.ERROR;
             bool success =
                 statement_get.bind_text (1, id, -1) == Sqlite.OK
-             && ((result = statement_get.step ())) == Sqlite.ROW;
+             && row_or_done (statement_get.step ());
             if (!success) {
                 statement_get.reset ();
                 throw new GLib.FileError.FAILED (_("Failed to get message: %s"), database.errmsg ());
@@ -150,15 +184,15 @@ namespace Postler {
         public string[] get_messages (string folder) throws GLib.Error {
             if (statement_list == null) {
                 if (database.prepare_v2 ("""
-                    SELECT id FROM messages WHERE uri LIKE ?1 ORDER BY date ASC
+                    SELECT id FROM messages WHERE uri LIKE ?1 GROUP BY id ORDER BY date ASC
                     """,
                     -1, out statement_list) != Sqlite.OK)
                     throw new GLib.FileError.FAILED (_("Failed to list messages: %s"), database.errmsg ());
             }
             int result = Sqlite.ERROR;
-            bool success = statement_list.bind_text (1, folder, -1) == Sqlite.OK;
-            result = statement_list.step ();
-            success = success && (result == Sqlite.ROW || result == Sqlite.DONE);
+            bool success =
+                statement_list.bind_text (1, folder, -1) == Sqlite.OK
+             && row_or_done (((result = statement_list.step ())));
             if (!success) {
                 statement_list.reset ();
                 throw new GLib.FileError.FAILED (_("Failed to list messages: %s"), database.errmsg ());
diff --git a/postler/postler-message.vala b/postler/postler-message.vala
index 20b10fa..b5c7b3b 100644
--- a/postler/postler-message.vala
+++ b/postler/postler-message.vala
@@ -10,6 +10,15 @@
 */
 
 namespace Postler {
+    public enum MessageFlags {
+        NONE = '0',
+        MARK_READ = '1',
+        TOGGLE_READ = 'S',
+        TOGGLE_FLAGGED = 'F',
+        TOGGLE_FORWARDED = 'P',
+        TOGGLE_REPLIED = 'R',
+    }
+
     public class Message : GLib.Object, GLib.Initable {
         public string? uri { public get; set; }
         public string get_path () { return GLib.File.new_for_uri (uri).get_path (); }
@@ -97,10 +106,10 @@ namespace Postler {
             /* format "unique:2,DFPRST", ordered alphabetically */
             unowned string? last_colon = path.rstr (":2,");
             if (last_colon != null) {
-                this.unread = last_colon.chr (-1, 'S') == null;
-                this.flagged = last_colon.chr (-1, 'F') != null;
-                this.forwarded = last_colon.chr (-1, 'P') != null;
-                this.replied = last_colon.chr (-1, 'R') != null;
+                this.unread = last_colon.chr (-1, MessageFlags.TOGGLE_READ) == null;
+                this.flagged = last_colon.chr (-1, MessageFlags.TOGGLE_FLAGGED) != null;
+                this.forwarded = last_colon.chr (-1, MessageFlags.TOGGLE_FORWARDED) != null;
+                this.replied = last_colon.chr (-1, MessageFlags.TOGGLE_REPLIED) != null;
             }
             else {
                 this.unread = true;
@@ -108,10 +117,17 @@ namespace Postler {
         }
 
         public void toggle_flag (char flag) {
+            if (flag == MessageFlags.NONE)
+                return;
+            if (flag == MessageFlags.MARK_READ && unread)
+                flag = MessageFlags.TOGGLE_READ;
+            else if (flag == MessageFlags.MARK_READ)
+                return;
             string old_path = get_path ();
             return_if_fail (old_path != null);
             string new_path = Postler.Messages.toggle_flag (old_path, flag);
             if (FileUtils.rename (old_path, new_path) == 0) {
+                uri = "file://" + new_path;
                 read_flags (new_path);
             }
         }
diff --git a/postler/postler-messages.vala b/postler/postler-messages.vala
index c2778b6..7039422 100644
--- a/postler/postler-messages.vala
+++ b/postler/postler-messages.vala
@@ -138,7 +138,7 @@ public class Postler.Messages : Gtk.TreeView {
         renderer.xalign = renderer.yalign = 0.0f;
 
         if (message.uri == null) {
-            new Client ().get_message.begin (message,
+            new Client ().get_message.begin (message, MessageFlags.NONE,
                 new Gtk.TreeRowReference (model, model.get_path (iter)));
             renderer.markup = "...";
             return;
@@ -166,9 +166,7 @@ public class Postler.Messages : Gtk.TreeView {
         if (!model.get_iter_from_string (out iter, path))
             return;
 
-        Message message;
-        model.get (iter, Columns.MESSAGE, out message);
-        message.toggle_flag ('F');
+        toggle_message_flag (iter, MessageFlags.TOGGLE_FLAGGED, () => { });
     }
 
     void renderer_status_toggled (Gtk.CellRendererToggle renderer,
@@ -177,9 +175,7 @@ public class Postler.Messages : Gtk.TreeView {
         if (!model.get_iter_from_string (out iter, path))
             return;
 
-        Message message;
-        model.get (iter, Columns.MESSAGE, out message);
-        message.toggle_flag ('S');
+        toggle_message_flag (iter, MessageFlags.TOGGLE_READ, () => { });
     }
 
     void on_drag_data_get (Gdk.DragContext context,
@@ -290,11 +286,9 @@ public class Postler.Messages : Gtk.TreeView {
     void content_new_window () {
         Gtk.TreeIter iter;
         if (get_selected_iter (out iter)) {
-            Message message;
-            model.get (iter, Columns.MESSAGE, out message);
-            if (message.unread)
-                message.toggle_flag ('S');
-            Postler.App.spawn_module ("content", message.get_path ());
+            toggle_message_flag (iter, MessageFlags.MARK_READ, (message, result) => {
+                Postler.App.spawn_module ("content", (message as Message).get_path ());
+            });
         }
     }
     [Signal (action=true)]
@@ -642,19 +636,23 @@ public class Postler.Messages : Gtk.TreeView {
             var path = reference.get_path ();
             Gtk.TreeIter iter;
             if (model.get_iter (out iter, path)) {
-                Message message;
-                model.get (iter, Columns.MESSAGE, out message);
-                message.toggle_flag (flag);
+                toggle_message_flag (iter, flag, () => { });
             }
         }
     }
 
     void display_message (Gtk.TreeIter iter) {
+        toggle_message_flag (iter, MessageFlags.MARK_READ, (message, result) => {
+            content.display ((message as Message).get_path (), account_info);
+        });
+    }
+
+    void toggle_message_flag (Gtk.TreeIter iter, char flag, AsyncReadyCallback callback) {
         Message message;
         model.get (iter, Columns.MESSAGE, out message);
-        if (message.unread)
-            message.toggle_flag ('S');
-        content.display (message.get_path (), account_info);
+        new Client ().get_message.begin (message, flag,
+            new Gtk.TreeRowReference (model, model.get_path (iter)),
+            (client, result) => { callback (message, result); });
     }
 
     public static bool ensure_folder (string folder) {
@@ -722,7 +720,7 @@ public class Postler.Messages : Gtk.TreeView {
         if ("S" in flags)
             filename = Path.get_basename (bare_filename) + ":" + flags;
         else
-            filename = Path.get_basename (toggle_flag (location, 'S'));
+            filename = Path.get_basename (toggle_flag (location, MessageFlags.TOGGLE_READ));
         string[] parts = filename.split (",");
         return_val_if_fail (parts[0] != null && parts[1] != null
             && parts[2] != null && parts[3] == null, "");
@@ -818,11 +816,10 @@ public class Postler.Messages : Gtk.TreeView {
         if (event.type == Gdk.EventType.2BUTTON_PRESS) {
             Gtk.TreeIter iter;
             if (get_selected_iter (out iter)) {
-                Message message;
-                model.get (iter, Columns.MESSAGE, out message);
-                if (message.unread)
-                    message.toggle_flag ('S');
-                Postler.App.spawn_module ("content", message.get_path ());
+                toggle_message_flag (iter, MessageFlags.MARK_READ,
+                    (message, result) => {
+                    Postler.App.spawn_module ("content", (message as Message).get_path ());
+                });
             }
         } else if (event.type == Gdk.EventType.BUTTON_PRESS
                 && get_selection ().count_selected_rows () > 1
diff --git a/postler/postler-service.vala b/postler/postler-service.vala
index cd8a480..45731d5 100644
--- a/postler/postler-service.vala
+++ b/postler/postler-service.vala
@@ -104,6 +104,7 @@ namespace Postler {
         int unread = 0;
         Dock.Item dockitem;
         Index? index = null;
+        FileMonitor[] folder_monitors = {};
 
 #if HAVE_INDICATE
         Indicate.Server indicator;
@@ -176,6 +177,32 @@ namespace Postler {
         }
 
         async void index_folder (File folder) throws GLib.Error {
+            var monitor = folder.monitor_directory (0, null);
+            folder_monitors += monitor;
+            monitor.changed.connect ((monitor, file, other, event) => {
+                try {
+                    switch (event) {
+                    case FileMonitorEvent.CREATED:
+                        var message = new Message.from_file (file);
+                        index.insert_received_message (message);
+                        break;
+                    case FileMonitorEvent.DELETED:
+                        index.remove_filename (file.get_uri ());
+                        break;
+                    case FileMonitorEvent.CHANGED:
+                    case FileMonitorEvent.CHANGES_DONE_HINT:
+                    case FileMonitorEvent.ATTRIBUTE_CHANGED:
+                        break;
+                    default:
+                        GLib.warning ("Unhandled folder change: " + event.to_string ());
+                        break;
+                    }
+                }
+                catch (GLib.Error monitor_error) {
+                    GLib.warning (_("Failed to index message \"%s\": %s"),
+                                  file.get_path (), monitor_error.message);
+                }
+            });
             var enumerator = yield folder.enumerate_children_async (
                 FILE_ATTRIBUTE_STANDARD_NAME, 0, Priority.DEFAULT);
             while (true) {
@@ -184,15 +211,15 @@ namespace Postler {
                     break;
                 foreach (var info in files) {
                     var file = folder.resolve_relative_path (info.get_name ());
-                    if (index.count_messages (file.get_uri ()) < 1) {
-                        try {
+                    try {
+                        if (!index.update_filename (file.get_uri ())) {
                             var message = new Message.from_file (file);
                             index.insert_received_message (message);
                         }
-                        catch (GLib.Error error) {
-                            GLib.warning (_("Failed to index message \"%s\": %s"),
-                                          file.get_path (), error.message);
-                        }
+                    }
+                    catch (GLib.Error error) {
+                        GLib.warning (_("Failed to index message \"%s\": %s"),
+                                      file.get_path (), error.message);
                     }
                 }
             }
@@ -206,6 +233,7 @@ namespace Postler {
                 var folder = File.new_for_path (account_info.path);
                 try {
                     var monitor = folder.monitor_directory (0, null);
+                    folder_monitors += monitor;
                     monitor.changed.connect ((monitor, file, other, event) => {            
                         /* TODO */
                     });
@@ -228,6 +256,12 @@ namespace Postler {
                         account_info.path, error.message);
                 }
             }
+            try {
+                index.purge_filenames ();
+            }
+            catch (GLib.Error purge_error) {
+                GLib.warning (_("Failed to purge old filenames: %s"), purge_error.message);
+            }
         }
 
         public PostlerService () {
@@ -332,8 +366,12 @@ namespace Postler {
              return index.unread_messages (uri);
         }
 
-        public GLib.HashTable<string,Variant> get_message (string id) throws GLib.Error {
-            return index.get_message (id).to_hash_table ();
+        public GLib.HashTable<string,Variant> get_message (string id,
+            char flag) throws GLib.Error {
+
+            var message = index.get_message (id);
+            message.toggle_flag (flag);
+            return message.to_hash_table ();
         }
 
         public string[] get_messages (string uri) throws GLib.Error {



More information about the Xfce4-commits mailing list