From dc12b73bbcc594c142277c0cf1017a8ffa0cf5f8 Mon Sep 17 00:00:00 2001
From: Patrick Simpson
Date: Tue, 14 Feb 2017 18:05:25 +0100
Subject: [PATCH] Reimplements folders and items events
---
.../AcaciaZPushPlugin.csproj | 4 +
.../Features/GAB/GABHandler.cs | 2 +-
.../AcaciaZPushPlugin/Stubs/IFolder.cs | 10 +-
.../AcaciaZPushPlugin/Stubs/IFolders.cs | 32 +++
.../AcaciaZPushPlugin/Stubs/IItems.cs | 34 +++
.../Stubs/OutlookWrappers/ComWrapper.cs | 3 +-
.../Stubs/OutlookWrappers/FolderWrapper.cs | 147 ++---------
.../Stubs/OutlookWrappers/FoldersWrapper.cs | 199 +++++++++++++++
.../Stubs/OutlookWrappers/ItemsWrapper.cs | 235 ++++++++++++++++++
.../AcaciaZPushPlugin/Stubs/Wrappers.cs | 20 ++
.../AcaciaZPushPlugin/Utils/Util.cs | 1 -
.../AcaciaZPushPlugin/ZPush/ZPushFolder.cs | 136 +++++++++-
.../ZPush/ZPushLocalStore.cs | 2 +-
13 files changed, 680 insertions(+), 145 deletions(-)
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
index 0092d93..8b97a68 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
@@ -286,7 +286,9 @@
+
+
@@ -296,7 +298,9 @@
+
+
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs
index bb3ad52..6fa50f5 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs
@@ -336,7 +336,7 @@ namespace Acacia.Features.GAB
// Scan a few of the newest items, in case there is some junk in the ZPush folder
// TODO: this shouldn't happen in production.
int i = 0;
- foreach(IItem item in Folder.ItemsSorted("LastModificationTime", true))
+ foreach(IItem item in Folder.Items.Sort("LastModificationTime", true))
{
using (item)
{
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs
index 54cdbd5..328df5b 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs
@@ -35,9 +35,7 @@ namespace Acacia.Stubs
bool ShowAsOutlookAB { get; set; }
- IEnumerable Items { get; }
-
- IEnumerable ItemsSorted(string field, bool descending);
+ IItems Items { get; }
IItem GetItemById(string id);
@@ -58,7 +56,11 @@ namespace Acacia.Stubs
IEnumerable GetSubFolders()
where FolderType : IFolder;
- IEnumerable GetSubFolders();
+
+ IFolders SubFolders
+ {
+ get;
+ }
FolderType GetSubFolder(string name)
where FolderType : IFolder;
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs
new file mode 100644
index 0000000..27fb82b
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Stubs
+{
+ public delegate void IFolders_FolderEventHandler(IFolder folder);
+ public delegate void IFolders_EventHandler();
+
+ public interface IFolders_Events : IDisposable
+ {
+ event IFolders_FolderEventHandler FolderAdd;
+ event IFolders_FolderEventHandler FolderChange;
+ event IFolders_EventHandler FolderRemove;
+ }
+
+ public interface IFolders : IEnumerable
+ {
+ #region Events
+
+
+ ///
+ /// Returns an events subscribption object.
+ ///
+ /// The events. The caller is responsible for disposing
+ IFolders_Events GetEvents();
+
+ #endregion
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs
new file mode 100644
index 0000000..9773f34
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Stubs
+{
+ public delegate void IItems_ItemEventHandler(IItem item);
+
+
+ public interface IItems_Events : IDisposable
+ {
+ event IItems_ItemEventHandler ItemAdd;
+ event IItems_ItemEventHandler ItemChange;
+ }
+
+ public interface IItems : IEnumerable
+ {
+ ///
+ /// Sorts the items.
+ ///
+ ///
+ ///
+ /// The current collection, which will be sorted
+ IItems Sort(string field, bool descending);
+
+ ///
+ /// Returns an events subscribption object.
+ ///
+ /// The events. The caller is responsible for disposing
+ IItems_Events GetEvents();
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs
index 9aac7ef..b8d4930 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs
@@ -27,7 +27,7 @@ namespace Acacia.Stubs.OutlookWrappers
{
abstract class ComWrapper : DisposableWrapper, IComWrapper
{
- protected ItemType _item { get; private set; }
+ protected readonly ItemType _item;
///
/// Creates a wrapper.
@@ -49,7 +49,6 @@ namespace Acacia.Stubs.OutlookWrappers
if (MustRelease)
{
ComRelease.Release(_item);
- _item = default(ItemType);
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
index f0bf96b..a8c934f 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
@@ -34,6 +34,13 @@ namespace Acacia.Stubs.OutlookWrappers
{
}
+ protected override void DoRelease()
+ {
+ base.DoRelease();
+ }
+
+ internal NSOutlook.Folder RawItem { get { return _item; } }
+
protected override NSOutlook.PropertyAccessor GetPropertyAccessor()
{
return _item.PropertyAccessor;
@@ -125,118 +132,15 @@ namespace Acacia.Stubs.OutlookWrappers
public ItemType ItemType { get { return (ItemType)(int)_item.DefaultItemType; } }
- #region Enumeration
- public class ItemsEnumerator : ComWrapper, IEnumerator
- where ItemType : IItem
- {
- private IEnumerator _enum;
- private ItemType _last;
-
- public ItemsEnumerator(NSOutlook.Folder folder, string field, bool descending) : base(folder.Items)
- {
- // TODO: can _items be released here already?
- if (field != null)
- {
- this._item.Sort("[" + field + "]", descending);
- }
- this._enum = _item.GetEnumerator();
- }
-
- protected override void DoRelease()
- {
- CleanLast();
- if (_enum != null)
- {
- if (_enum is IDisposable)
- ((IDisposable)_enum).Dispose();
- ComRelease.Release(_enum);
- _enum = null;
- }
- base.DoRelease();
- }
-
- public ItemType Current
- {
- get
- {
- CleanLast();
- _last = Mapping.Wrap(_enum.Current);
- return _last;
- }
- }
-
- object IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
-
- private void CleanLast()
- {
- if (_last != null)
- {
- _last.Dispose();
- _last = default(ItemType);
- }
- }
-
- public bool MoveNext()
- {
- CleanLast();
- return _enum.MoveNext();
- }
-
- public void Reset()
- {
- CleanLast();
- _enum.Reset();
- }
- }
-
- public class ItemsEnumerable : IEnumerable
- where ItemType : IItem
- {
- // Managed by the caller, not released here
- private readonly NSOutlook.Folder _folder;
- private readonly string _field;
- private readonly bool _descending;
-
- public ItemsEnumerable(NSOutlook.Folder folder, string field, bool descending)
- {
- this._folder = folder;
- this._field = field;
- this._descending = descending;
- }
-
- public IEnumerator GetEnumerator()
- {
- return new ItemsEnumerator(_folder, _field, _descending);
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- public IEnumerable Items
+ public IItems Items
{
get
{
- return new ItemsEnumerable(_item, null, false);
+ return new ItemsWrapper(this);
}
}
- public IEnumerable ItemsSorted(string field, bool descending)
- {
- return new ItemsEnumerable(_item, field, descending);
- }
-
- #endregion
-
public IItem GetItemById(string entryId)
{
try
@@ -289,13 +193,16 @@ namespace Acacia.Stubs.OutlookWrappers
// Don't release the items, the wrapper manages them
foreach (NSOutlook.Folder folder in _item.Folders.RawEnum(false))
{
- yield return WrapFolder(folder);
+ yield return folder.Wrap();
};
}
- public IEnumerable GetSubFolders()
+ public IFolders SubFolders
{
- return GetSubFolders();
+ get
+ {
+ return new FoldersWrapper(this);
+ }
}
public FolderType GetSubFolder(string name)
@@ -319,7 +226,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
if (sub == null)
return default(FolderType);
- return WrapFolder(sub);
+ return sub.Wrap();
}
public FolderType CreateFolder(string name)
@@ -330,37 +237,19 @@ namespace Acacia.Stubs.OutlookWrappers
NSOutlook.Folders folders = com.Add(_item.Folders);
if (typeof(FolderType) == typeof(IFolder))
{
- return WrapFolder(folders.Add(name));
+ return folders.Add(name).Wrap();
}
else if (typeof(FolderType) == typeof(IAddressBook))
{
NSOutlook.MAPIFolder newFolder = folders.Add(name, NSOutlook.OlDefaultFolders.olFolderContacts);
newFolder.ShowAsOutlookAB = true;
- return WrapFolder(newFolder);
+ return newFolder.Wrap();
}
else
throw new NotSupportedException();
}
}
- private FolderType WrapFolder(NSOutlook.MAPIFolder folder)
- where FolderType : IFolder
- {
- if (typeof(FolderType) == typeof(IFolder))
- {
- return (FolderType)(IFolder)new FolderWrapper(folder);
- }
- else if (typeof(FolderType) == typeof(IAddressBook))
- {
- return (FolderType)(IFolder)new AddressBookWrapper(folder);
- }
- else
- {
- ComRelease.Release(folder);
- throw new NotSupportedException();
- }
- }
-
#endregion
public IStorageItem GetStorageItem(string name)
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs
new file mode 100644
index 0000000..cfe3031
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs
@@ -0,0 +1,199 @@
+using Acacia.Utils;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NSOutlook = Microsoft.Office.Interop.Outlook;
+
+namespace Acacia.Stubs.OutlookWrappers
+{
+ class FoldersWrapper : IFolders
+ {
+ // Managed by the caller, not released here
+ private readonly FolderWrapper _folder;
+
+ public FoldersWrapper(FolderWrapper folder)
+ {
+ this._folder = folder;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ // Don't release the items, the wrapper manages them
+ foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false))
+ {
+ yield return folder.Wrap();
+ };
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ // Don't release the items, the wrapper manages them
+ foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false))
+ {
+ yield return folder.Wrap();
+ };
+ }
+
+ #region Events
+
+ private class EventsWrapper : ComWrapper, IFolders_Events
+ {
+ public EventsWrapper(NSOutlook.Folders item) : base(item)
+ {
+ }
+
+ #region FolderAdd
+
+ private IFolders_FolderEventHandler _folderAdd;
+ public event IFolders_FolderEventHandler FolderAdd
+ {
+ add
+ {
+ if (_folderAdd == null)
+ HookFolderAdd(true);
+ _folderAdd += value;
+ }
+ remove
+ {
+ _folderAdd -= value;
+ if (_folderAdd == null)
+ HookFolderAdd(false);
+ }
+ }
+
+ private void HookFolderAdd(bool hook)
+ {
+ if (hook)
+ _item.FolderAdd += HandleFolderAdd;
+ else
+ _item.FolderAdd -= HandleFolderAdd;
+ }
+
+ private void HandleFolderAdd(NSOutlook.MAPIFolder folder)
+ {
+ try
+ {
+ if (_folderAdd != null)
+ {
+ using (IFolder folderWrapped = Mapping.Wrap(folder, false))
+ {
+ if (folderWrapped != null)
+ {
+ _folderAdd(folderWrapped);
+ }
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in HandleFolderAdd: {0}", e);
+ }
+ }
+
+ #endregion
+
+ #region FolderChange
+
+ private IFolders_FolderEventHandler _folderChange;
+ public event IFolders_FolderEventHandler FolderChange
+ {
+ add
+ {
+ if (_folderChange == null)
+ HookFolderChange(true);
+ _folderChange += value;
+ }
+ remove
+ {
+ _folderChange -= value;
+ if (_folderChange == null)
+ HookFolderChange(false);
+ }
+ }
+
+ private void HookFolderChange(bool hook)
+ {
+ if (hook)
+ _item.FolderChange += HandleFolderChange;
+ else
+ _item.FolderChange -= HandleFolderChange;
+ }
+
+ private void HandleFolderChange(NSOutlook.MAPIFolder folder)
+ {
+ try
+ {
+ if (_folderChange != null)
+ {
+ using (IFolder folderWrapped = Mapping.Wrap(folder, false))
+ {
+ if (folderWrapped != null)
+ {
+ _folderChange(folderWrapped);
+ }
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in HandleFolderChange: {0}", e);
+ }
+ }
+
+ #endregion
+
+ #region FolderRemove
+
+ private IFolders_EventHandler _folderRemove;
+ public event IFolders_EventHandler FolderRemove
+ {
+ add
+ {
+ if (_folderRemove == null)
+ HookFolderRemove(true);
+ _folderRemove += value;
+ }
+ remove
+ {
+ _folderRemove -= value;
+ if (_folderRemove == null)
+ HookFolderRemove(false);
+ }
+ }
+
+ private void HookFolderRemove(bool hook)
+ {
+ if (hook)
+ _item.FolderRemove += HandleFolderRemove;
+ else
+ _item.FolderRemove -= HandleFolderRemove;
+ }
+
+ private void HandleFolderRemove()
+ {
+ try
+ {
+ if (_folderRemove != null)
+ {
+ _folderRemove();
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in HandleFolderRemove: {0}", e);
+ }
+ }
+
+ #endregion
+ }
+
+ public IFolders_Events GetEvents()
+ {
+ return new EventsWrapper(_folder.RawItem.Folders);
+ }
+ #endregion
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs
new file mode 100644
index 0000000..c088e84
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs
@@ -0,0 +1,235 @@
+using Acacia.Utils;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NSOutlook = Microsoft.Office.Interop.Outlook;
+
+namespace Acacia.Stubs.OutlookWrappers
+{
+ class ItemsWrapper : IItems
+ {
+ // Managed by the caller, not released here
+ private readonly FolderWrapper _folder;
+ private string _field;
+ private bool _descending;
+
+ public ItemsWrapper(FolderWrapper folder)
+ {
+ this._folder = folder;
+ }
+
+ public IItems Sort(string field, bool descending)
+ {
+ this._field = field;
+ this._descending = descending;
+ return this;
+ }
+
+ private NSOutlook.Items GetItems()
+ {
+ return _folder.RawItem.Items;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return new ItemsEnumerator(this, _field, _descending);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #region Enumeration
+
+ public class ItemsEnumerator : ComWrapper, IEnumerator
+ where ItemType : IItem
+ {
+ private IEnumerator _enum;
+ private ItemType _last;
+
+ public ItemsEnumerator(ItemsWrapper items, string field, bool descending) : base(items.GetItems())
+ {
+ // TODO: can _items be released here already?
+ if (field != null)
+ {
+ this._item.Sort("[" + field + "]", descending);
+ }
+ this._enum = _item.GetEnumerator();
+ }
+
+ protected override void DoRelease()
+ {
+ CleanLast();
+ if (_enum != null)
+ {
+ if (_enum is IDisposable)
+ ((IDisposable)_enum).Dispose();
+ ComRelease.Release(_enum);
+ _enum = null;
+ }
+ base.DoRelease();
+ }
+
+ public ItemType Current
+ {
+ get
+ {
+ CleanLast();
+ _last = Mapping.Wrap(_enum.Current);
+ return _last;
+ }
+ }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ private void CleanLast()
+ {
+ if (_last != null)
+ {
+ _last.Dispose();
+ _last = default(ItemType);
+ }
+ }
+
+ public bool MoveNext()
+ {
+ CleanLast();
+ return _enum.MoveNext();
+ }
+
+ public void Reset()
+ {
+ CleanLast();
+ _enum.Reset();
+ }
+ }
+
+ #endregion
+
+ #region Events
+
+ private class EventsWrapper : ComWrapper, IItems_Events
+ {
+ public EventsWrapper(NSOutlook.Items item) : base(item)
+ {
+ }
+
+ #region ItemAdd
+
+ private IItems_ItemEventHandler _itemAdd;
+ public event IItems_ItemEventHandler ItemAdd
+ {
+ add
+ {
+ if (_itemAdd == null)
+ HookItemAdd(true);
+ _itemAdd += value;
+ }
+ remove
+ {
+ _itemAdd -= value;
+ if (_itemAdd == null)
+ HookItemAdd(false);
+ }
+ }
+
+ private void HookItemAdd(bool hook)
+ {
+ if (hook)
+ _item.ItemAdd += HandleItemAdd;
+ else
+ _item.ItemAdd -= HandleItemAdd;
+ }
+
+ private void HandleItemAdd(object objItem)
+ {
+ try
+ {
+ if (_itemAdd != null)
+ {
+ using (IItem item = Mapping.Wrap(objItem, false))
+ {
+ if (item != null)
+ {
+ _itemAdd(item);
+ }
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in HandleItemAdd: {0}", e);
+ }
+ }
+
+ #endregion
+
+ #region ItemChange
+
+ private IItems_ItemEventHandler _itemChange;
+ public event IItems_ItemEventHandler ItemChange
+ {
+ add
+ {
+ if (_itemChange == null)
+ HookItemChange(true);
+ _itemChange += value;
+ }
+ remove
+ {
+ _itemChange -= value;
+ if (_itemChange == null)
+ HookItemChange(false);
+ }
+ }
+
+ private void HookItemChange(bool hook)
+ {
+ if (hook)
+ _item.ItemChange += HandleItemChange;
+ else
+ _item.ItemChange -= HandleItemChange;
+ }
+
+ private void HandleItemChange(object objItem)
+ {
+ try
+ {
+ if (_itemChange != null)
+ {
+ using (IItem item = Mapping.Wrap(objItem, false))
+ {
+ if (item != null)
+ {
+ _itemChange(item);
+ }
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in HandleItemChange: {0}", e);
+ }
+ }
+
+ #endregion
+ }
+
+ public IItems_Events GetEvents()
+ {
+ return new EventsWrapper(GetItems());
+ }
+
+ #endregion
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs
index a30c9a7..d163089 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs
@@ -1,4 +1,5 @@
using Acacia.Stubs.OutlookWrappers;
+using Acacia.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,6 +16,25 @@ namespace Acacia.Stubs
return Mapping.WrapOrDefault(obj);
}
+
+ public static FolderType Wrap(this NSOutlook.MAPIFolder folder)
+ where FolderType : IFolder
+ {
+ if (typeof(FolderType) == typeof(IFolder))
+ {
+ return (FolderType)(IFolder)new FolderWrapper(folder);
+ }
+ else if (typeof(FolderType) == typeof(IAddressBook))
+ {
+ return (FolderType)(IFolder)new AddressBookWrapper(folder);
+ }
+ else
+ {
+ ComRelease.Release(folder);
+ throw new NotSupportedException();
+ }
+ }
+
public static WrapType Wrap(this object o, bool mustRelease = true)
where WrapType : IBase
{
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs
index dbef980..62453e7 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs
@@ -62,7 +62,6 @@ namespace Acacia.Utils
ComRelease.Release(source);
}
- // TODO: check this
public static IEnumerable RawEnum(this IEnumerable source, bool releaseItems = true)
{
foreach (object item in source)
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs
index c5941c4..9f6fad2 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs
@@ -27,10 +27,12 @@ namespace Acacia.ZPush
{
public class ZPushFolder : DisposableWrapper
{
- private IFolder _folder;
- private ZPushFolder _parent;
+ private readonly IFolder _folder;
+ private readonly IItems_Events _items;
+ private readonly IFolders_Events _subFolders;
+ private readonly ZPushFolder _parent;
private readonly ZPushWatcher _watcher;
- private List _itemsWatchers = new List();
+ private readonly List _itemsWatchers = new List();
///
/// Children folders indexed by EntryID
@@ -50,6 +52,9 @@ namespace Acacia.ZPush
this._parent = parent;
this._watcher = watcher;
this._folder = folder;
+ // We need to keep links to these objects to keep getting events.
+ this._items = folder.Items.GetEvents();
+ this._subFolders = folder.SubFolders.GetEvents();
folder.ZPush = this;
}
@@ -57,6 +62,13 @@ namespace Acacia.ZPush
{
Cleanup();
_folder.Dispose();
+ _items.Dispose();
+ _subFolders.Dispose();
+ }
+
+ public override string ToString()
+ {
+ return Name;
}
public IFolder Folder { get { return _folder; } }
@@ -71,7 +83,7 @@ namespace Acacia.ZPush
_watcher.OnFolderDiscovered(this);
// Recurse the children
- foreach (IFolder subfolder in _folder.GetSubFolders())
+ foreach (IFolder subfolder in _folder.SubFolders)
{
Tasks.Task(null, "WatchChild", () => WatchChild(subfolder));
}
@@ -94,10 +106,10 @@ namespace Acacia.ZPush
}
}
+ #region Event handlers
+
private void HookEvents(bool register)
{
- // TODO
- /*
if (register)
{
// Item events
@@ -119,9 +131,119 @@ namespace Acacia.ZPush
_subFolders.FolderAdd -= SubFolders_FolderAdd;
_subFolders.FolderRemove -= SubFolders_FolderRemove;
_subFolders.FolderChange -= SubFolders_FolderChange;
- }*/
+ }
}
+ #region Event handlers
+
+ private void SubFolders_FolderAdd(IFolder folder)
+ {
+ try
+ {
+ Logger.Instance.Debug(this, "Folder added in {0}: {1}", Name, folder.Name);
+ WatchChild(folder);
+ }
+ catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderAdd: {0}: {1}", Name, e); }
+ }
+
+ private void SubFolders_FolderRemove()
+ {
+ try
+ {
+ Logger.Instance.Debug(this, "Folder removed from {0}", Name);
+
+ // Helpfully, Outlook doesn't tell us which folder was removed. Could use the BeforeFolderMove event instead,
+ // but that doesn't fire if a folder was removed on the server.
+ // Hence, fetch all the remaining folder ids, and remove any folder that no longer exists.
+ // TODO: move this logic into IFolders?
+ HashSet remaining = new HashSet();
+ foreach (IFolder child in _folder.SubFolders)
+ {
+ try
+ {
+ remaining.Add(child.EntryID);
+ }
+ catch (System.Exception e) { Logger.Instance.Warning(this, "Ignoring failed child: {0}", e); }
+ }
+
+ // Find the folders that need to be removed. There should be only one, but with Outlook we can never be sure,
+ // so compare all. We cannot modify the dictionary during iteration, so store entries to be removed in a
+ // temporary list
+ List> remove = new List>();
+ foreach (var entry in _children)
+ {
+ if (!remaining.Contains(entry.Key))
+ {
+ remove.Add(entry);
+ }
+ }
+
+ // Actually remove the folders
+ foreach (var entry in remove)
+ {
+ Logger.Instance.Debug(this, "Removing subfolder {0}, {1}", Name, entry.Key);
+ _children.Remove(entry.Key);
+ entry.Value.Cleanup();
+ }
+ }
+ catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderRemove: {0}: {1}", Name, e); }
+ }
+
+ private void SubFolders_FolderChange(IFolder folder)
+ {
+ try
+ {
+ Logger.Instance.Debug(this, "Folder changed in {0}: {1}", Name, folder.Name);
+ ZPushFolder child;
+ if (_children.TryGetValue(folder.EntryID, out child))
+ {
+ _watcher.OnFolderChanged(child);
+ // TODO: release folder?
+ }
+ else
+ {
+ // On a clean profile, we sometimes get a change notification, but not an add notification
+ // Create it now
+ // This will send a discover notification if required, which is just as good as a change notification
+ Logger.Instance.Debug(this, "Folder change on unreported folder in {0}: {1}, {2}, {3}", Name, folder.Name, folder.EntryID, folder.StoreDisplayName);
+ WatchChild(folder);
+ }
+ }
+ catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderChange: {0}: {1}", Name, e); }
+ }
+
+ private void Items_ItemAdd(IItem item)
+ {
+ try
+ {
+ Logger.Instance.Trace(this, "New item {0}: {1}", Name, item.EntryID);
+ foreach (ItemsWatcher watcher in _itemsWatchers)
+ watcher.OnItemAdd(this, item);
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Trace(this, "ItemAdd exception: {0}: {1}", Name, e);
+ }
+ }
+
+ private void Items_ItemChange(IItem item)
+ {
+ try
+ {
+ Logger.Instance.Trace(this, "Changed item {0}", Name);
+ foreach (ItemsWatcher watcher in _itemsWatchers)
+ watcher.OnItemChange(this, item);
+ }
+ catch (System.Exception e)
+ {
+ Logger.Instance.Trace(this, "ItemChange exception: {0}: {1}", Name, e);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
private void Cleanup()
{
Logger.Instance.Trace(this, "Unwatching folder: {0}", _folder.Name);
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs
index 9330eb5..268ca1f 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs
@@ -135,7 +135,7 @@ namespace Acacia.ZPush
// Hide the folders that are not custom folders
using (IFolder root = store.GetRootFolder())
{
- foreach(IFolder sub in root.GetSubFolders())
+ foreach(IFolder sub in root.SubFolders)
{
using (sub)
{