[geary/wip/721828-undo] Working round-trip of undo with archive and trash
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/721828-undo] Working round-trip of undo with archive and trash
- Date: Wed, 31 Dec 2014 01:11:44 +0000 (UTC)
commit 7abfc1fb8d4ce215a47ae88bc937d03fc9a89183
Author: Jim Nelson <jim yorba org>
Date: Tue Dec 30 17:11:28 2014 -0800
Working round-trip of undo with archive and trash
src/engine/app/app-conversation.vala | 8 ++-
src/engine/imap-db/imap-db-folder.vala | 54 ++++++++++++----
.../imap-engine/imap-engine-batch-operations.vala | 2 +-
.../imap-engine/imap-engine-minimal-folder.vala | 36 ++++++-----
.../imap-engine/imap-engine-revokable-move.vala | 6 +-
.../replay-ops/imap-engine-copy-email.vala | 13 +---
.../replay-ops/imap-engine-move-email.vala | 69 ++++++++++++++++----
src/engine/imap/api/imap-folder.vala | 31 +++++++--
src/engine/util/util-collection.vala | 4 +
9 files changed, 156 insertions(+), 67 deletions(-)
---
diff --git a/src/engine/app/app-conversation.vala b/src/engine/app/app-conversation.vala
index 121a707..3a05693 100644
--- a/src/engine/app/app-conversation.vala
+++ b/src/engine/app/app-conversation.vala
@@ -209,7 +209,7 @@ public class Geary.App.Conversation : BaseObject {
* known_paths should contain all the known FolderPaths this email is contained in.
* Conversation will monitor Account for additions and removals as they occur.
*/
- internal bool add(Email email, Gee.Collection<Geary.FolderPath> known_paths) {
+ internal bool add(Email email, Gee.Collection<Geary.FolderPath>? known_paths) {
if (emails.has_key(email.id))
return false;
@@ -221,8 +221,10 @@ public class Geary.App.Conversation : BaseObject {
if (ancestors != null)
message_ids.add_all(ancestors);
- foreach (Geary.FolderPath path in known_paths)
- path_map.set(email.id, path);
+ if (known_paths != null) {
+ foreach (Geary.FolderPath path in known_paths)
+ path_map.set(email.id, path);
+ }
appended(email);
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index 91d50a8..d382c3f 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -727,13 +727,43 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
return id;
}
- public async void detach_multiple_emails_async(Gee.Collection<ImapDB.EmailIdentifier> ids,
+ // EmailIdentifiers should contain valid UIDs *for this folder* for successful linking.
+ public async void link_multiple_emails_async(Gee.Collection<ImapDB.EmailIdentifier> ids,
+ Cancellable? cancellable) throws Error {
+ int unread_count = 0;
+ yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
+ // Can't use do_get_locations_for_ids, as that's keyed to this folder, which these
+ // emails don't belong to yet
+ foreach (ImapDB.EmailIdentifier id in ids) {
+ if (id.uid == null)
+ continue;
+
+ Db.Statement stmt = cx.prepare("""
+ INSERT INTO MessageLocationTable
+ (message_id, folder_id, ordering)
+ VALUES (?, ?, ?)
+ """);
+ stmt.bind_rowid(0, id.message_id);
+ stmt.bind_rowid(1, folder_id);
+ stmt.bind_int64(2, id.uid.value);
+
+ stmt.exec(cancellable);
+ }
+
+ // Now with messages linked to folder, update unread count
+ unread_count = do_get_unread_count_for_ids(cx, ids, cancellable);
+ do_add_to_unread_count(cx, unread_count, cancellable);
+
+ return Db.TransactionOutcome.COMMIT;
+ }, cancellable);
+
+ if (unread_count > 0)
+ properties.set_status_unseen(properties.email_unread + unread_count);
+ }
+
+ public async void unlink_multiple_emails_async(Gee.Collection<ImapDB.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
int unread_count = 0;
- // TODO: Right now, deleting an email is merely detaching its association with a folder
- // (since it may be located in multiple folders). This means at some point in the future
- // a vacuum will be required to remove emails that are completely unassociated with the
- // account.
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
Gee.List<LocationIdentifier>? locs = do_get_locations_for_ids(cx, ids,
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
@@ -766,7 +796,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
properties.set_status_unseen(properties.email_unread - unread_count);
}
- public async void detach_all_emails_async(Cancellable? cancellable) throws Error {
+ public async void unlink_all_emails_async(Cancellable? cancellable) throws Error {
yield db.exec_transaction_async(Db.TransactionType.WO, (cx) => {
Db.Statement stmt = cx.prepare(
"DELETE FROM MessageLocationTable WHERE folder_id=?");
@@ -899,7 +929,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
}
}
- public async void detach_single_email_async(ImapDB.EmailIdentifier id, out bool is_marked,
+ public async void unlink_single_email_async(ImapDB.EmailIdentifier id, out bool is_marked,
Cancellable? cancellable) throws Error {
bool internal_is_marked = false;
bool was_unread = false;
@@ -920,7 +950,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
internal_is_marked = location.marked_removed;
- do_remove_association_with_folder(cx, location, cancellable);
+ do_unlink_from_folder(cx, location, cancellable);
return Db.TransactionOutcome.COMMIT;
}, cancellable);
@@ -1225,7 +1255,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
}
// Note: does NOT check if message is already associated with thie folder
- private void do_associate_with_folder(Db.Connection cx, int64 message_id, Imap.UID uid,
+ private void do_link_with_folder(Db.Connection cx, int64 message_id, Imap.UID uid,
Cancellable? cancellable) throws Error {
assert(message_id != Db.INVALID_ROWID);
@@ -1239,7 +1269,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.exec(cancellable);
}
- private void do_remove_association_with_folder(Db.Connection cx, LocationIdentifier location,
+ private void do_unlink_from_folder(Db.Connection cx, LocationIdentifier location,
Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare(
"DELETE FROM MessageLocationTable WHERE folder_id=? AND message_id=?");
@@ -1261,7 +1291,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
// if found, merge, and associate if necessary
if (location != null) {
if (!associated)
- do_associate_with_folder(cx, location.message_id, location.uid, cancellable);
+ do_link_with_folder(cx, location.message_id, location.uid, cancellable);
// If the email came from the Imap layer, we need to fill in the id.
ImapDB.EmailIdentifier email_id = (ImapDB.EmailIdentifier) email.id;
@@ -1331,7 +1361,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
if (email_id.message_id == Db.INVALID_ROWID)
email_id.promote_with_message_id(message_id);
- do_associate_with_folder(cx, message_id, uid, cancellable);
+ do_link_with_folder(cx, message_id, uid, cancellable);
// write out attachments, if any
// TODO: Because this involves saving files, it potentially means holding up access to the
diff --git a/src/engine/imap-engine/imap-engine-batch-operations.vala
b/src/engine/imap-engine/imap-engine-batch-operations.vala
index 904d2fe..6aae979 100644
--- a/src/engine/imap-engine/imap-engine-batch-operations.vala
+++ b/src/engine/imap-engine/imap-engine-batch-operations.vala
@@ -71,7 +71,7 @@ private class Geary.ImapEngine.RemoveLocalEmailOperation : Geary.Nonblocking.Bat
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
- yield folder.detach_multiple_emails_async((Gee.Collection<ImapDB.EmailIdentifier>) email_ids,
cancellable);
+ yield folder.unlink_multiple_emails_async((Gee.Collection<ImapDB.EmailIdentifier>) email_ids,
cancellable);
return null;
}
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index c3956de..9089df6 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -109,11 +109,11 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
}
// used by normalize_folders() during the normalization process; should not be used elsewhere
- private async void detach_all_emails_async(Cancellable? cancellable) throws Error {
+ private async void unlink_all_emails_async(Cancellable? cancellable) throws Error {
Gee.List<Email>? all = yield local_folder.list_email_by_id_async(null, -1,
Geary.Email.Field.NONE, ImapDB.Folder.ListFlags.NONE, cancellable);
- yield local_folder.detach_all_emails_async(cancellable);
+ yield local_folder.unlink_all_emails_async(cancellable);
if (all != null && all.size > 0) {
Gee.List<EmailIdentifier> ids =
@@ -159,7 +159,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
local_properties.uid_validity.value.to_string(),
remote_properties.uid_validity.value.to_string());
- yield detach_all_emails_async(cancellable);
+ yield unlink_all_emails_async(cancellable);
return true;
}
@@ -215,7 +215,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
to_string(), local_earliest_id.uid.to_string(), local_latest_id.uid.to_string(),
last_uid.to_string());
- yield detach_all_emails_async(cancellable);
+ yield unlink_all_emails_async(cancellable);
return true;
}
@@ -380,7 +380,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
removed_ids = yield local_folder.get_ids_async(removed_uids,
ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
if (removed_ids != null && removed_ids.size > 0) {
- yield local_folder.detach_multiple_emails_async(removed_ids, cancellable);
+ yield local_folder.unlink_multiple_emails_async(removed_ids, cancellable);
}
}
@@ -1065,7 +1065,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
owned_id.to_string());
try {
// Reflect change in the local store and notify subscribers
- yield local_folder.detach_single_email_async(owned_id, out marked, null);
+ yield local_folder.unlink_single_email_async(owned_id, out marked, null);
} catch (Error err) {
debug("%s do_replay_removed_message: unable to remove message #%s: %s", to_string(),
remote_position.to_string(), err.message);
@@ -1271,6 +1271,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
replay_queue.schedule(move);
yield move.wait_for_ready_async(cancellable);
+ // No destination EmailIdentifiers, no way to revoke (undo) this operation
+ if (Collection.is_empty(move.destination_ids))
+ return null;
+
return new RevokableMove(_account, path, destination, move.destination_ids);
}
@@ -1369,26 +1373,24 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
return ret;
}
- // To be used only by RevokableMove. Return false if UIDs are unknown to local store.
- internal async bool revoke_move_async(Gee.Collection<Imap.UID> uids, FolderPath source,
+ // To be used only by RevokableMove.
+ internal async void revoke_move_async(Gee.Collection<ImapDB.EmailIdentifier> ids, FolderPath source,
Cancellable? cancellable) throws Error {
check_open("revoke_move_async");
- // need to wait for fully open to ensure UIDs are normalized between local and remove
- yield wait_for_open_async(cancellable);
-
- Gee.Set<ImapDB.EmailIdentifier>? ids = yield local_folder.get_ids_async(uids,
- ImapDB.Folder.ListFlags.NONE, cancellable);
- if (ids == null || ids.size == 0)
- return false;
+ // revoking back to this folder is a no-no
+ if (source.equal_to(path)) {
+ warning("[%s] Attempted to revoke move to source same as destination (%s)", to_string(),
+ source.to_string());
+
+ return;
+ }
MoveEmail move = new MoveEmail(this, traverse<ImapDB.EmailIdentifier>(ids).to_array_list(),
source, cancellable);
replay_queue.schedule(move);
yield move.wait_for_ready_async(cancellable);
-
- return true;
}
}
diff --git a/src/engine/imap-engine/imap-engine-revokable-move.vala
b/src/engine/imap-engine/imap-engine-revokable-move.vala
index f670116..64e9a5e 100644
--- a/src/engine/imap-engine/imap-engine-revokable-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-move.vala
@@ -57,11 +57,11 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
// open, revoke, close, ensuring the close and signal disconnect are performed in all cases
try {
- yield dest_folder.open_async(Geary.Folder.OpenFlags.NO_DELAY, cancellable);
+ yield dest_folder.open_async(Geary.Folder.OpenFlags.NONE, cancellable);
// watch out for messages detected as gone when folder is opened
- if (can_revoke && !yield dest_folder.revoke_move_async(destination_ids, original_source,
- cancellable)) {
+ if (can_revoke) {
+ yield dest_folder.revoke_move_async(destination_ids, original_source, cancellable);
can_revoke = false;
}
} finally {
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
index 42ccc09..d12b5de 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
@@ -47,18 +47,9 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
ImapDB.Folder.ListFlags.NONE, cancellable);
if (uids != null && uids.size > 0) {
- Gee.Set<Imap.UID> acc_uids = new Gee.HashSet<Imap.UID>();
-
Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(uids);
- foreach (Imap.MessageSet msg_set in msg_sets) {
- Gee.Set<Imap.UID>? dest_uids = yield engine.remote_folder.copy_email_async(msg_set,
- destination, cancellable);
- if (dest_uids != null)
- acc_uids.add_all(dest_uids);
- }
-
- if (acc_uids.size > 0)
- destination_uids = acc_uids;
+ foreach (Imap.MessageSet msg_set in msg_sets)
+ yield engine.remote_folder.copy_email_async(msg_set, destination, cancellable);
}
return ReplayOperation.Status.COMPLETED;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
index 30b19a4..aa89846 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
@@ -5,13 +5,14 @@
*/
private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation {
- public Gee.Set<ImapDb.EmailIdentifier>? destination_ids { get; private set; default = null; }
+ public Gee.Set<ImapDB.EmailIdentifier>? destination_ids { get; private set; default = null; }
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_move = new Gee.ArrayList<ImapDB.EmailIdentifier>();
private Geary.FolderPath destination;
private Cancellable? cancellable;
private Gee.Set<ImapDB.EmailIdentifier>? moved_ids = null;
+ private Gee.Set<ImapDB.EmailIdentifier>? predicted_ids = null;
private int original_count = 0;
public MoveEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_move,
@@ -52,11 +53,18 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
engine.notify_email_count_changed(Numeric.int_floor(original_count - to_move.size, 0),
Geary.Folder.CountChangeReason.REMOVED);
+ // Report to the local destination Folder that new messages are predicted to arrive
MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
as MinimalFolder;
if (dest_folder != null) {
- debug("NOTIFY PREDICTED: %d", moved_ids.size);
- dest_folder.notify_predict_email_inserted(moved_ids);
+ // convert moved identifiers to prediction identifiers, which have no UID (that comes
+ // after the network portion of the operation finishes) ... this allows for open folders
+ // to report new mail without waiting for the operation to finish
+ predicted_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+ foreach (ImapDB.EmailIdentifier moved_id in moved_ids)
+ predicted_ids.add(new ImapDB.EmailIdentifier(moved_id.message_id, null));
+
+ dest_folder.notify_predict_email_inserted(predicted_ids);
}
return ReplayOperation.Status.CONTINUE;
@@ -76,23 +84,54 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
if (cancellable != null && cancellable.is_cancelled())
throw new IOError.CANCELLED("Move email to %s cancelled", engine.remote_folder.to_string());
- // TODO: Need to generate fully-valid ImapDB.EmailIdentifiers for the destination folder
- // (with message_ids and UIDs properly associated)
+ // Use this accumulator through the iteration of message set(s) and then store it all at
+ // once in destination_ids
Gee.Set<ImapDB.EmailIdentifier> acc_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+ // Create a map of UIDs to moved_ids to make lookup easy when generating destination ids
+ Gee.Map<Imap.UID, ImapDB.EmailIdentifier> source_uid_to_source_id =
+ traverse<ImapDB.EmailIdentifier>(moved_ids)
+ .to_hash_map<Imap.UID>(id => id.uid);
+
Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
ImapDB.EmailIdentifier.to_uids(moved_ids));
foreach (Imap.MessageSet msg_set in msg_sets) {
- Gee.Set<Imap.UID>? dest_uids = yield engine.remote_folder.copy_email_async(msg_set,
+ Gee.Map<Imap.UID, Imap.UID>? copyuids = yield engine.remote_folder.copy_email_async(msg_set,
destination, null);
- if (dest_uids != null)
- acc_uids.add_all(dest_uids);
+
+ // convert returned COPYUID response into EmailIdentifiers for the destination folder
+ if (copyuids != null && copyuids.size > 0) {
+ Gee.MapIterator<Imap.UID, Imap.UID> iter = copyuids.map_iterator();
+ while (iter.next()) {
+ Imap.UID source_uid = iter.get_key();
+ if (source_uid_to_source_id.has_key(source_uid)) {
+ ImapDB.EmailIdentifier source_id = source_uid_to_source_id[source_uid];
+
+ acc_ids.add(new ImapDB.EmailIdentifier(source_id.message_id, iter.get_value()));
+ }
+ }
+ }
yield engine.remote_folder.remove_email_async(msg_set, null);
}
- if (acc_uids.size > 0)
- destination_uids = acc_uids;
+ if (acc_ids.size > 0) {
+ // link the destination id's to the local folder, which saves a little trouble when
+ // normalizing with the remote as well as makes revoking them work properly
+ MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
+ as MinimalFolder;
+ if (dest_folder != null) {
+ try {
+ // Note that this doesn't require dest_folder be open because we're going straight
+ // to the database...dest_folder will announce to its subscribers changes when
+ // normalization/notification occurs via IMAP
+ yield dest_folder.local_folder.link_multiple_emails_async(acc_ids, cancellable);
+ destination_ids = acc_ids;
+ } catch (Error err) {
+ debug("Unable to link moved emails to new folder: %s", err.message);
+ }
+ }
+ }
return ReplayOperation.Status.COMPLETED;
}
@@ -103,10 +142,12 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
engine.notify_email_inserted(moved_ids);
engine.notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
- MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
- as MinimalFolder;
- if (dest_folder != null)
- dest_folder.notify_unpredict_email_inserted(moved_ids);
+ if (!Collection.is_empty(predicted_ids)) {
+ MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
+ as MinimalFolder;
+ if (dest_folder != null)
+ dest_folder.notify_unpredict_email_inserted(predicted_ids);
+ }
}
public override string describe_state() {
diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala
index d5bde97..b07f447 100644
--- a/src/engine/imap/api/imap-folder.vala
+++ b/src/engine/imap/api/imap-folder.vala
@@ -675,7 +675,9 @@ private class Geary.Imap.Folder : BaseObject {
yield exec_commands_async(cmds, null, null, cancellable);
}
- public async Gee.List<UID>? copy_email_async(MessageSet msg_set, Geary.FolderPath destination,
+ // Returns a mapping of the source UID to the destination UID. If the MessageSet is not for
+ // UIDs, then null is returned. If the server doesn't support COPYUID, null is returned.
+ public async Gee.Map<UID, UID>? copy_email_async(MessageSet msg_set, Geary.FolderPath destination,
Cancellable? cancellable) throws Error {
check_open();
@@ -689,16 +691,33 @@ private class Geary.Imap.Folder : BaseObject {
return null;
StatusResponse response = responses.get(cmd);
- if (response.response_code != null) {
- Gee.List<UID>? destination_uids = null;
+ if (response.response_code != null && msg_set.is_uid) {
+ Gee.List<UID>? src_uids = null;
+ Gee.List<UID>? dst_uids = null;
try {
- response.response_code.get_copyuid(null, null, out destination_uids);
+ response.response_code.get_copyuid(null, out src_uids, out dst_uids);
} catch (ImapError ierr) {
debug("Unable to retrieve COPYUID UIDs: %s", ierr.message);
}
- if (destination_uids != null && destination_uids.size > 0)
- return Geary.traverse<UID>(destination_uids).to_hash_set();
+ if (!Collection.is_empty(src_uids) && !Collection.is_empty(dst_uids)) {
+ Gee.Map<UID, UID> copyuids = new Gee.HashMap<UID, UID>();
+ int ctr = 0;
+ for (;;) {
+ UID? src_uid = (ctr < src_uids.size) ? src_uids[ctr] : null;
+ UID? dst_uid = (ctr < dst_uids.size) ? dst_uids[ctr] : null;
+
+ if (src_uid != null && dst_uid != null)
+ copyuids.set(src_uid, dst_uid);
+ else
+ break;
+
+ ctr++;
+ }
+
+ if (copyuids.size > 0)
+ return copyuids;
+ }
}
return null;
diff --git a/src/engine/util/util-collection.vala b/src/engine/util/util-collection.vala
index 294de11..e374021 100644
--- a/src/engine/util/util-collection.vala
+++ b/src/engine/util/util-collection.vala
@@ -8,6 +8,10 @@ namespace Geary.Collection {
public delegate uint8 ByteTransformer(uint8 b);
+public bool is_empty(Gee.Collection? c) {
+ return c == null || c.size == 0;
+}
+
// A substitute for ArrayList<G>.wrap() for compatibility with older versions of Gee.
public Gee.ArrayList<G> array_list_wrap<G>(G[] a, owned Gee.EqualDataFunc<G>? equal_func = null) {
Gee.ArrayList<G> list = new Gee.ArrayList<G>(equal_func);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]