Updating branch refs/heads/master to b692fea99435518d8713298edf90a75b95c11638 (commit) from 87496d12d29a2e7feed193412e7b382c0ac6b25b (commit)
commit b692fea99435518d8713298edf90a75b95c11638 Author: Christian Dywan <christ...@twotoasts.de> Date: Mon May 23 23:39:59 2011 +0200 Expose UnreadMessages, GetMessages, GetMessage via DBus Messages are defined in terms of URIs, not necessarily filenames and the frontend only uses the Message API for reading. postler/postler-client.vala | 22 +++ postler/postler-content.vala | 5 - postler/postler-folders.vala | 27 ++-- postler/postler-messages.vala | 359 +++++++++-------------------------------- 4 files changed, 114 insertions(+), 299 deletions(-) diff --git a/postler/postler-client.vala b/postler/postler-client.vala index 20f69da..d07a782 100644 --- a/postler/postler-client.vala +++ b/postler/postler-client.vala @@ -13,6 +13,9 @@ namespace Postler { [DBus (name = "org.elementary.Postler")] interface PostlerClient : Object { + public abstract int64 unread_messages (string uri) throws IOError; + public async abstract GLib.HashTable<string,Variant> get_message (string uri) throws IOError; + public 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; public signal void received (string account, string error_message); @@ -47,6 +50,25 @@ namespace Postler { } catch (GLib.Error error) { } } + public int64 unread_messages (string uri) throws GLib.Error { + return client.unread_messages (uri); + } + + public async void get_message (Message message, Gtk.TreeRowReference row) throws GLib.Error { + message.from_hash_table (yield client.get_message (message.uri)); + Gtk.TreeIter iter; + if (row.get_model ().get_iter (out iter, row.get_path ())) + row.get_model ().row_changed (row.get_path (), iter); + } + + public async GLib.List<Message> get_messages (string uri) throws GLib.Error { + var messages = new GLib.List<Message> (); + string[] uris = client.get_messages (uri); + foreach (string message_uri in uris) + messages.append (new Message.from_uri (message_uri)); + return messages; + } + public signal void progress (string account, string? text, double fraction); public void receive (string account="") { diff --git a/postler/postler-content.vala b/postler/postler-content.vala index 20db52b..0797765 100644 --- a/postler/postler-content.vala +++ b/postler/postler-content.vala @@ -458,11 +458,6 @@ public class Postler.Content : WebKit.WebView { return the_time.format (_("%B %e, %Y") + (" %X")); } - internal static string format_timestamp (int64 timestamp, int offset=0) { - var the_time = new DateTime.from_unix_local (timestamp).add_minutes (offset); - return format_date (the_time); - } - internal static DateTime date_from_string (string date) { var parsed = new Soup.Date.from_string (date); TimeVal time = new TimeVal (); diff --git a/postler/postler-folders.vala b/postler/postler-folders.vala index 13c0b3f..6d5175b 100644 --- a/postler/postler-folders.vala +++ b/postler/postler-folders.vala @@ -212,20 +212,16 @@ public class Postler.Folders : Gtk.TreeView { void unread_count_update (Gtk.TreeIter iter, File msg_dir, string label) { try { - var msg_enumerator = msg_dir.enumerate_children ("", 0, null); - int unread = 0; - FileInfo info; - while ((info = msg_enumerator.next_file (null)) != null) - unread++; + int64 unread = new Postler.Client ().unread_messages (msg_dir.get_uri () + "%"); string escaped = GLib.Markup.escape_text (label); if (unread == 0) store.set (iter, Columns.DISPLAY_NAME, "%s".printf (escaped)); else if (msg_dir.get_path ().has_suffix ("INBOX/new")) store.set (iter, Columns.DISPLAY_NAME, - "<b>%s (%d)</b>".printf (escaped, unread)); + "<b>%s (%d)</b>".printf (escaped, (int)unread)); else store.set (iter, Columns.DISPLAY_NAME, - "%s (%d)".printf (escaped, unread)); + "%s (%d)".printf (escaped, (int)unread)); } catch (GLib.Error error) { GLib.critical (_("Failed to monitor folder \"%s\": %s"), msg_dir.get_path (), error.message); @@ -262,7 +258,8 @@ public class Postler.Folders : Gtk.TreeView { var account_infos = accounts.get_infos ().copy (); if (local_info.path != null) account_infos.prepend (local_info); - return populate_accounts (account_infos); + populate_accounts (account_infos.copy ()); + return false; } bool get_folder_iter (string location, Gtk.TreeIter parent_iter, @@ -305,7 +302,7 @@ public class Postler.Folders : Gtk.TreeView { return name; } - public bool populate_accounts (GLib.List<AccountInfo> infos) { + public async void populate_accounts (owned GLib.List<AccountInfo> infos) { bool need_update = false; /* foreach (unowned AccountInfo account_info in infos) { */ @@ -326,6 +323,7 @@ public class Postler.Folders : Gtk.TreeView { try { var folder_dir = File.new_for_path (account_info.path); + GLib.FileMonitor monitor; Gtk.TreeIter account_iter; bool existing_iter = false; @@ -349,11 +347,11 @@ public class Postler.Folders : Gtk.TreeView { store.remove (iter); } else { - var monitor = folder_dir.monitor_directory (0, null); + monitor = folder_dir.monitor_directory (0, null); monitor.changed.connect ((monitor, file, other, event) => { var account_infos = new GLib.List<AccountInfo> (); account_infos.prepend (account_info); - populate_accounts (account_infos); + populate_accounts (account_infos.copy ()); }); store.insert_with_values (out account_iter, null, -1, Columns.ICON, Gtk.STOCK_DIALOG_ERROR, @@ -419,7 +417,7 @@ public class Postler.Folders : Gtk.TreeView { if (name == "INBOX") { var msg_dir = folder_dir.resolve_relative_path ( account_info.path + "/" + name + "/new"); - var monitor = msg_dir.monitor_directory (0, null); + monitor = msg_dir.monitor_directory (0, null); string label = account_info.display_name; monitor.changed.connect ((monitor, file, other, event) => { unread_monitor_changed (msg_dir, label); @@ -437,11 +435,11 @@ public class Postler.Folders : Gtk.TreeView { } var account_dir = folder_dir.resolve_relative_path (name); - var monitor = account_dir.monitor_directory (0, null); + monitor = account_dir.monitor_directory (0, null); monitor.changed.connect ((monitor, file, other, event) => { var account_infos = new GLib.List<AccountInfo> (); account_infos.prepend (account_info); - populate_accounts (account_infos); + populate_accounts (account_infos.copy ()); }); unowned MailFolder folder = account_info.get_localized_folder (name); @@ -503,7 +501,6 @@ public class Postler.Folders : Gtk.TreeView { if (need_update) accounts.update (); } - return false; } public bool select_folder (string folder, Gtk.TreeIter? parent_iter=null) diff --git a/postler/postler-messages.vala b/postler/postler-messages.vala index 4ee1433..e933e1b 100644 --- a/postler/postler-messages.vala +++ b/postler/postler-messages.vala @@ -18,7 +18,6 @@ public class Postler.Messages : Gtk.TreeView { public Postler.Content content { get; set; } public bool hide_read { get; set; } - public bool rich_rows { get; set; default = true; } public string? location { get; private set; } public AccountInfo account_info { get; private set; } @@ -31,20 +30,15 @@ public class Postler.Messages : Gtk.TreeView { FileMonitor[] folder_monitors = {}; enum Columns { - FLAGGED, - STATUS, - SUBJECT, - WEIGHT, - FROM, - LOCATION, - TIMESTAMP + MESSAGE, + LOCATION } bool search_inline (Gtk.TreeModel model, int column, string key, Gtk.TreeIter iter) { - string subject; - model.get (iter, Columns.SUBJECT, out subject); - return !(key in subject.down ()); + Message message; + model.get (iter, Columns.MESSAGE, out message); + return !(key in message.subject.down ()); } void selection_changed () { @@ -91,15 +85,26 @@ public class Postler.Messages : Gtk.TreeView { return escaped.str; } + void render_status (Gtk.CellLayout layout, Gtk.CellRenderer cell, + Gtk.TreeModel model, Gtk.TreeIter iter) { + + Message message; + + model.get (iter, Columns.MESSAGE, out message); + + cell.set ("stock-id", message.unread ? STOCK_MAIL_UNREAD : null); + /* TODO: missing status values */ + } + void render_flag (Gtk.CellLayout layout, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { - bool flagged; + Message message; var screen = get_screen (); unowned string stock_id = null; unowned string prelight_stock_id = null; - model.get (iter, Columns.FLAGGED, out flagged); - if (flagged) { + model.get (iter, Columns.MESSAGE, out message); + if (message.flagged) { if (Gtk.IconTheme.get_for_screen (screen).has_icon (STOCK_STARRED)) stock_id = STOCK_STARRED; else @@ -108,23 +113,20 @@ public class Postler.Messages : Gtk.TreeView { if (Gtk.IconTheme.get_for_screen (screen).has_icon (STOCK_NOT_STARRED)) prelight_stock_id = STOCK_NOT_STARRED; } - cell.set ("stock-id", stock_id, "prelight-stock-id", prelight_stock_id); + cell.set ("active", message.flagged, + "stock-id", stock_id, "prelight-stock-id", prelight_stock_id); } void render_subject (Gtk.CellLayout layout, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { - string charset, subject; - int weight = Pango.Weight.NORMAL; - int64 timestamp; + + Message message; var renderer = cell as Gtk.CellRendererText; - model.get (iter, Columns.SUBJECT, out subject, - Columns.WEIGHT, out weight, - Columns.TIMESTAMP, out timestamp); + model.get (iter, Columns.MESSAGE, out message); - if (timestamp == 0) { - model.get (iter, Columns.SUBJECT, out subject); - renderer.markup = subject; + if (message.uri == "error:") { + renderer.markup = message.subject; renderer.xpad = renderer.ypad = 8; renderer.xalign = renderer.yalign = 0.5f; return; @@ -133,24 +135,22 @@ public class Postler.Messages : Gtk.TreeView { renderer.xpad = renderer.ypad = 0; renderer.xalign = renderer.yalign = 0.0f; - if (!rich_rows) { - model.get (iter, Columns.WEIGHT, out weight); - renderer.text = parse_encoded (subject, out charset); - renderer.weight = weight; + if (message.date == null) { + new Client ().get_message.begin (message, + new Gtk.TreeRowReference (model, model.get_path (iter))); + renderer.markup = "..."; return; } - string from; - model.get (iter, Columns.FROM, out from); - - subject = escape_text (parse_encoded (subject, out charset)); - string date = Postler.Content.format_timestamp (timestamp); + string subject = escape_text (message.subject ?? _("No subject")); + string date = Postler.Content.format_date (message.date); long size = date.length; for (int j = 0; j < 20 - size; j++) date = date + " "; - from = escape_text (parse_address (parse_encoded (from, out charset))[0]); + string from = escape_text (parse_address (message.sender ?? _("Unknown"))[0]); from = dexter.get_name (from) ?? from; + int weight = message.unread ? Pango.Weight.BOLD : Pango.Weight.NORMAL; renderer.ellipsize = Pango.EllipsizeMode.MIDDLE; renderer.markup = ("<span weight=\"%d\">%s</span>\n" + @@ -158,36 +158,6 @@ public class Postler.Messages : Gtk.TreeView { weight, from, date, subject); } - void render_from (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, - Gtk.TreeModel model, Gtk.TreeIter iter) { - var renderer = cell as Gtk.CellRendererText; - - if (rich_rows) - renderer.text = ""; - else { - string? from; - model.get (iter, Columns.FROM, out from); - if (from != null) { - string charset; - from = parse_address (parse_encoded (from, out charset))[0]; - } - renderer.text = from; - } - } - - void render_date (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, - Gtk.TreeModel model, Gtk.TreeIter iter) { - var renderer = cell as Gtk.CellRendererText; - - if (rich_rows) - renderer.text = ""; - else { - int64 timestamp; - model.get (iter, Columns.TIMESTAMP, out timestamp); - renderer.text = Postler.Content.format_timestamp (timestamp); - } - } - void renderer_flag_toggled (Gtk.CellRendererToggle renderer, string path) { Gtk.TreeIter sort_iter = Gtk.TreeIter (); @@ -234,9 +204,7 @@ public class Postler.Messages : Gtk.TreeView { drag_data_get.connect (on_drag_data_get); this.accounts = accounts; - store = new Gtk.TreeStore (7, typeof (bool), typeof (string), - typeof (string), typeof (int), typeof (string), - typeof (string), typeof (int64)); + store = new Gtk.TreeStore (2, typeof (Message), typeof (string)); sort = new Gtk.TreeModelSort.with_model (store); set_model (sort); set_search_equal_func (search_inline); @@ -247,12 +215,13 @@ public class Postler.Messages : Gtk.TreeView { var renderer_flag = new Postler.CellRendererToggle (); column.pack_start (renderer_flag, true); column.set_cell_data_func (renderer_flag, render_flag); - column.add_attribute (renderer_flag, "active", Columns.FLAGGED); renderer_flag.toggled.connect (renderer_flag_toggled); insert_column (column, -1); + column = new Gtk.TreeViewColumn (); + column.set_title (_("Status")); var renderer_status = new Postler.CellRendererToggle (); - insert_column_with_attributes (-1, _("Status"), - renderer_status, "stock-id", Columns.STATUS); + column.pack_start (renderer_status, true); + column.set_cell_data_func (renderer_status, render_status); renderer_status.toggled.connect (renderer_status_toggled); column = new Gtk.TreeViewColumn (); column.set_title (_("Subject")); @@ -260,15 +229,6 @@ public class Postler.Messages : Gtk.TreeView { column.pack_start (renderer_text, true); column.set_cell_data_func (renderer_text, render_subject); insert_column (column, -1); - insert_column_with_data_func (-1, _("From"), - new Gtk.CellRendererText (), render_from); - insert_column_with_data_func (-1, _("Date"), - new Gtk.CellRendererText (), render_date); - - if (rich_rows) { - get_column (3).visible = false; - get_column (4).visible = false; - } unowned Gtk.BindingSet binding_set = Gtk.BindingSet.by_class (get_class ()); Gtk.BindingEntry.add_signal (binding_set, @@ -489,123 +449,7 @@ public class Postler.Messages : Gtk.TreeView { populate (location, account_info, filter.down (), header); } - private struct Message { - string location; - string subject; - string status; - int font_weight; - bool flagged; - string from; - int64 timestamp; - } - - private Message? read_message (File contents, bool folder_new) { - - string filename = contents.get_basename (); - if (filename[0] == '.') - return null; - - string status = STOCK_MAIL_UNREAD; - int font_weight = Pango.Weight.BOLD; - string flags = null; - if (!folder_new) { - status = parse_flags (filename, out flags, out font_weight); - if (hide_read && font_weight != Pango.Weight.BOLD && flags == null) - return null; - if (flags != null && flags[0] == 'T') - return null; - } - - var message = new Message (); - message.status = status; - message.font_weight = font_weight; - message.flagged = flags != null; - message.location = contents.get_path (); - message.subject = null; - message.from = null; - message.timestamp = 0; - - string content_type = ""; - try { - var stream = new DataInputStream (contents.read (null)); - string line; - string previous_line = ""; - while ((line = stream.read_line (null, null)) != null) { - if (line == "") - break; - if (line[0] == '\t' || line[0] == ' ') - line = previous_line + " " + line.chug (); - previous_line = line; - - string[] parts = line.split (":", 2); - if (parts == null || parts[0] == null) - continue; - string field = ascii_strdown (parts[0]); - if (filters[0] != null && parts[1] != null) { - string lowercased = ascii_strdown (parts[1]); - if (headers[0] == field - && !(filters[0] in lowercased)) - return null; - else if (filters[1] != null && headers[1] == field - && !(filters[1] in lowercased)) - return null; - } - if (field == "subject") { - message.subject = parts[1].strip (); - if (message.from != null && message.timestamp != 0 - && content_type != null && filters[0] == null) - break; - } - else if (field == to_or_from) { - message.from = parts[1]; - if (message.subject != null && message.timestamp != 0 - && content_type != null && filters[0] == null) - break; - } - else if (field == "date") { - var the_time = Postler.Content.date_from_string (parts[1]); - message.timestamp = the_time.to_local ().to_unix (); - if (message.subject != null && message.from != null - && content_type != null && filters[0] == null) - break; - } - else if (field == "content-type") { - content_type = parts[1].strip (); - if (message.subject != null && message.from != null - && message.timestamp != 0 && filters[0] == null) - break; - } - } - - unowned string? fulltext = null; - if (headers[0] == "fulltext") - fulltext = filters[0]; - else if (headers[1] == "fulltext") - fulltext = filters[1]; - if (fulltext != null) { - bool skip = true; - while ((line = stream.read_line (null, null)) != null) { - if (line.down ().contains (fulltext)) { - skip = false; - break; - } - } - if (skip) - return null; - } - if (message.subject == null) - message.subject = _("No subject"); - if (message.from == null) - message.from = _("Unknown"); - } catch (GLib.Error contents_error) { - GLib.critical (_("Failed to read message \"%s\": %s"), - contents.get_path (), contents_error.message); - } - - return message; - } - - public bool populate (string? location, AccountInfo account_info, + public async bool populate (string? location, AccountInfo account_info, string? filter=null, string? header=null) { clear (); if (location == null) @@ -614,7 +458,8 @@ public class Postler.Messages : Gtk.TreeView { && !FileUtils.test (location + "/cur", FileTest.EXISTS)) return true; - model = sort = null; + /* FIXME no actual sorting occurs, we can stop using a sortable */ + model = sort = new Gtk.TreeModelSort.with_model (store); var now = GLib.Date (); now.set_time_val (GLib.TimeVal ()); @@ -626,22 +471,19 @@ public class Postler.Messages : Gtk.TreeView { to_or_from = "to"; else to_or_from = "from"; + var messages = new GLib.List<Message> (); try { if (location != this.location) { folders = {}; headers = {}; filters = {}; - if (location.has_prefix ("search:")) { - foreach (var folder in accounts.get_folders ()) { - folders += folder + "/INBOX/new"; - folders += folder + "/INBOX/cur"; - } - } - else { - folders += location + "/cur"; - folders += location + "/new"; - } + var client = new Client (); + /* TODO: pass search: URI */ + if (location.has_prefix ("search:")) + messages = yield client.get_messages ("%/INBOX/%"); + else + messages = yield client.get_messages ("file://" + location + "%"); } if (filters[0] == null) { @@ -659,46 +501,10 @@ public class Postler.Messages : Gtk.TreeView { } } - foreach (var folder in folders) { - var folder_dir = File.new_for_path (folder); - FileEnumerator folder_enumerator; - try { - folder_enumerator = folder_dir.enumerate_children ( - FILE_ATTRIBUTE_STANDARD_NAME, 0, null); - } catch (GLib.Error enumerator_error) { - /* Empty accounts can't be enumerated. */ - continue; - } - GLib.FileInfo info; - - try { - var folder_monitor = folder_dir.monitor_directory (0, null); - folder_monitor.changed.connect (folder_monitor_changed); - folder_monitors += folder_monitor; - } catch (GLib.Error monitor_error) { - GLib.critical (_("Failed to monitor folder \"%s\": %s"), - folder, monitor_error.message); - } - - folder = folder_dir.get_basename (); - bool folder_new = folder_dir.get_path ().has_suffix ("new"); - while ((info = folder_enumerator.next_file (null)) != null) { - unowned string name = info.get_name (); - - var message_path = folder_dir.resolve_relative_path (name); - var message = read_message (message_path, folder_new); - if (message == null) - continue; - - store.insert_with_values (null, null, 0, - Columns.FLAGGED, message.flagged, - Columns.STATUS, message.status, - Columns.SUBJECT, message.subject, - Columns.WEIGHT, message.font_weight, - Columns.FROM, message.from, - Columns.TIMESTAMP, message.timestamp, - Columns.LOCATION, message.location); - } + foreach (var message in messages) { + store.insert_with_values (null, null, 0, + Columns.MESSAGE, message, + Columns.LOCATION, message.get_path ()); } /* Show error for failed search, ie. no results */ @@ -712,10 +518,6 @@ public class Postler.Messages : Gtk.TreeView { get_selection().set_select_function( () => { return true; }); } - sort = new Gtk.TreeModelSort.with_model (store); - sort.set_sort_column_id (Columns.TIMESTAMP, Gtk.SortType.DESCENDING); - model = sort; - hadjustment.value = vadjustment.value = 0; } catch (GLib.Error error) { @@ -730,14 +532,16 @@ public class Postler.Messages : Gtk.TreeView { return false; } - public void display_error (string title, string message) { + public void display_error (string title, string text) { clear (); - string markup = "<big><b>%s</b></big>\n\n%s".printf (title, message); + string markup = "<big><b>%s</b></big>\n\n%s".printf (title, text); + var message_data = new GLib.HashTable<string,Variant> (str_hash, str_equal); + message_data.insert ("subject", markup); + var message = new Message.from_uri ("error:"); + message.from_hash_table (message_data); store.insert_with_values (null, null, 0, - Columns.SUBJECT, markup, - Columns.FROM, null); - sort = new Gtk.TreeModelSort.with_model (store); - model = sort; + Columns.MESSAGE, message); + model = sort = new Gtk.TreeModelSort.with_model (store); } internal static string toggle_flag (string location, char flag) { @@ -769,6 +573,7 @@ public class Postler.Messages : Gtk.TreeView { void toggle_message_flag (Gtk.TreeIter sort_iter, ref string location, char flag) { return_if_fail (location != null); string new_location = toggle_flag (location, flag); + /* TODO update message file */ if (FileUtils.rename (location, new_location) == 0) { location = new_location; int font_weight = Pango.Weight.BOLD; @@ -778,10 +583,7 @@ public class Postler.Messages : Gtk.TreeView { Gtk.TreeIter child_iter; sort.convert_iter_to_child_iter (out child_iter, sort_iter); store.set (child_iter, - Columns.FLAGGED, flagged != null ? true : false, - Columns.STATUS, status, - Columns.LOCATION, new_location, - Columns.WEIGHT, font_weight); + Columns.LOCATION, new_location); if (location == selected_location) selected_location = new_location; } @@ -828,9 +630,9 @@ public class Postler.Messages : Gtk.TreeView { model.iter_nth_child (out iter, null, model.iter_n_children (null) - 1); while ((forward && model.iter_next (ref iter)) || (!forward && iter_previous (ref iter))) { - int font_weight; - model.get (iter, Columns.WEIGHT, out font_weight); - if (font_weight == Pango.Weight.BOLD) { + Message message; + model.get (iter, Columns.MESSAGE, out message); + if (message.unread) { set_cursor (model.get_path (iter), null, false); break; } @@ -842,21 +644,20 @@ public class Postler.Messages : Gtk.TreeView { case FileMonitorEvent.CREATED: if (get_message_iter (file.get_path (), null)) break; - bool folder_new = file.get_path ().has_suffix ("/new"); - var message = read_message (file, folder_new); - if (message == null) - break; - bool scroll = vadjustment.value == 0; - store.insert_with_values (null, null, 0, - Columns.FLAGGED, message.flagged, - Columns.STATUS, message.status, - Columns.SUBJECT, message.subject, - Columns.WEIGHT, message.font_weight, - Columns.FROM, message.from, - Columns.TIMESTAMP, message.timestamp, - Columns.LOCATION, message.location); - if (scroll) - vadjustment.value = 0; + try { + /* TODO: Honor filter */ + /* TODO: sort by timestamp */ + var message = new Message.from_file (file); + bool scroll = vadjustment.value == 0; + store.insert_with_values (null, null, 0, + Columns.MESSAGE, message, + Columns.LOCATION, message.get_path ()); + if (scroll) + vadjustment.value = 0; + } + catch (GLib.Error error) { + GLib.warning ("Failed to get new message: %s", error.message); + } break; case FileMonitorEvent.DELETED: Gtk.TreeIter iter; _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org http://foo-projects.org/mailman/listinfo/xfce4-commits